import { z } from "zod/v4";
import { fn } from "../utils/fn.js";
import { Identifier } from "../id.js";
import { userTable } from "./user.sql.js";
import { Examples } from "../examples.js";
import { Polar } from "../polar/index.js";
import { Database } from "../drizzle/index.js";
import { eq, and, isNotNull } from "drizzle-orm";

export namespace User {
  export const Info = z
    .object({
      id: Identifier.schema("user").meta({
        description: "The unique identifier of the user",
        example: Examples.User.id,
      }),
      name: z.string().nullable().meta({
        description: "The full name of the user",
        example: Examples.User.name,
      }),
      email: z.email().meta({
        description: "The email address of the user",
        example: Examples.User.email,
      }),
      customerID: z.string().nullable().meta({
        description: "Associated Polar.sh customer identifier",
        example: Examples.User.customerID,
      }),
    })
    .meta({
      ref: "User",
      description: "Represents a user entity",
      example: Examples.User,
    });

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

  export const create = fn(
    Info.omit({ customerID: true }).partial({ id: true }),
    async (input) => {
      const id = Identifier.ascending("user", input.id);

      const customerID = await Polar.fromUserEmail(input.email).then(
        (z) => z.id,
      );

      await Database.transaction((tx) =>
        tx
          .insert(userTable)
          .values({
            id,
            customerID,
            name: input.name,
            email: input.email,
          })
          .onConflictDoUpdate({
            target: userTable.email,
            set: {
              timeDeleted: null,
            },
          }),
      );

      return id;
    },
  );

  export const fromEmail = fn(Info.shape.email, (email) =>
    Database.transaction((tx) =>
      tx
        .select()
        .from(userTable)
        .where(
          and(eq(userTable.email, email), isNotNull(userTable.timeDeleted)),
        )
        .then((rows) => rows.map(serialize).at(0)),
    ),
  );

  export const fromID = fn(Info.shape.id, (id) =>
    Database.transaction((tx) =>
      tx
        .select()
        .from(userTable)
        .where(and(eq(userTable.id, id), isNotNull(userTable.timeDeleted)))
        .then((rows) => rows.map(serialize).at(0)),
    ),
  );

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