import type { Doc, Id } from "./_generated/dataModel";
import { mutation, query } from "./_generated/server";
import { requireAuthUserId } from "./authz";

interface OrganizationView {
  readonly _id: Id<"organizations">;
  readonly _creationTime: number;
  readonly name: string;
  readonly kind: "personal" | "team";
  readonly createdBy: string;
  readonly createdAt: number;
}

const toView = (org: Doc<"organizations">): OrganizationView => ({
  _creationTime: org._creationTime,
  _id: org._id,
  createdAt: org.createdAt,
  createdBy: org.createdBy,
  kind: org.kind,
  name: org.name,
});

/**
 * Idempotently create (or return) the authenticated user's single personal
 * organization plus an owner membership. Convex mutations are atomic
 * transactions: if two concurrent calls both observe no existing org and both
 * insert, the transaction's optimistic-concurrency check rejects one; the
 * retried call re-reads and returns the now-existing org. The read+write is the
 * whole transaction, so no duplicate personal org can persist.
 */
export const ensurePersonalOrganization = mutation({
  args: {},
  handler: async (ctx): Promise<OrganizationView> => {
    const ownerId = await requireAuthUserId(ctx);
    const existing = await ctx.db
      .query("organizations")
      .withIndex("by_createdBy_and_kind", (q) =>
        q.eq("createdBy", ownerId).eq("kind", "personal")
      )
      .unique();
    if (existing) {
      return toView(existing);
    }
    const createdAt = Date.now();
    const organizationId = await ctx.db.insert("organizations", {
      createdAt,
      createdBy: ownerId,
      kind: "personal",
      name: "Personal",
    });
    await ctx.db.insert("organizationMembers", {
      createdAt,
      organizationId,
      role: "owner",
      userId: ownerId,
    });
    const created = await ctx.db.get(organizationId);
    if (!created) {
      throw new Error("Failed to create personal organization");
    }
    return toView(created);
  },
});

/**
 * Return the authenticated user's current organization. In the single personal
 * organization phase this is the user's one personal org. Returns null when the
 * user has not yet ensured one. Denies unauthenticated access.
 */
export const getCurrent = query({
  args: {},
  handler: async (ctx): Promise<OrganizationView | null> => {
    const ownerId = await requireAuthUserId(ctx);
    const existing = await ctx.db
      .query("organizations")
      .withIndex("by_createdBy_and_kind", (q) =>
        q.eq("createdBy", ownerId).eq("kind", "personal")
      )
      .unique();
    return existing ? toView(existing) : null;
  },
});
