import { z } from "zod/v4";
import { fn } from "../utils/fn";
import { Examples } from "../examples.js";
import { createID } from "../utils/id.js";
import { Polar } from "../polar/index.js";
import { Database } from "../drizzle/index.js";
import { teamTable } from "../team/team.sql.js";
import { and, asc, eq, isNull } from "drizzle-orm";
import { VisibleError, ErrorCodes } from "../error.js";

export namespace Team {
  export const Info = z
    .object({
      id: z.string().meta({
        description: "The unique identifier of the team",
        example: Examples.Team.id,
      }),
      email: z.email().meta({
        description: "The contact email address for the team",
        example: Examples.Team.email,
      }),
      polarCustomerID: z.string().nullable().meta({
        description: "Associated Polar.sh customer identifier",
        example: Examples.Team.polarCustomerID,
      }),
    })
    .meta({
      ref: "Team",
      description: "Represents a team entity",
      example: Examples.Team,
    });

  export type Info = z.infer<typeof Info>;

  export class TeamExistsError extends VisibleError {
    constructor(email: string) {
      super(
        "already_exists",
        ErrorCodes.Validation.ALREADY_EXISTS,
        `A team with this email ${email} already exists`,
      );
    }
  }

  export const create = fn(
    Info.omit({ polarCustomerID: true }).partial({ id: true }),
    async (input) => {
      const userID = createID("team");
      const customerID = await Polar.fromUserEmail(input.email).then(
        (polarUser) => polarUser?.id || null,
      );

      const id = input.id ?? userID;

      await Database.transaction(async (tx) => {
        const result = await tx
          .insert(teamTable)
          .values({
            id,
            email: input.email,
            polarCustomerID: customerID,
          })
          .onConflictDoNothing({
            target: [teamTable.email],
          });

        if (result.count === 0) {
          throw new TeamExistsError(input.email);
        }
      });

      return id;
    },
  );

  export const fromEmail = fn(Info.shape.email, async (email) =>
    Database.transaction(async (tx) =>
      tx
        .select()
        .from(teamTable)
        .where(and(eq(teamTable.email, email), isNull(teamTable.timeDeleted)))
        .orderBy(asc(teamTable.timeCreated))
        .execute()
        .then((rows) => rows.map(serialize).at(0)),
    ),
  );

  export function serialize(
    input: typeof teamTable.$inferSelect,
  ): z.infer<typeof Info> {
    return {
      id: input.id,
      email: input.email,
      polarCustomerID: input.polarCustomerID,
    };
  }
}
