import { relations, sql } from "drizzle-orm";
import { pgTableCreator, index, primaryKey } from "drizzle-orm/pg-core";
import { type AdapterAccount } from "next-auth/adapters";
import { createId } from "@paralleldrive/cuid2";
import type { LobbyMemberRole } from "@/lib/validations/lobby";
/**
 * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
 * database instance for multiple projects.
 *
 * @see https://orm.drizzle.team/docs/goodies#multi-project-schema
 */
export const createTable = pgTableCreator((name) => `game-master_${name}`);

const defaultTimeStamp = (name: string, d: any) =>
  d
    .timestamp(name, { withTimezone: true })
    .default(sql`CURRENT_TIMESTAMP`)
    .notNull();

export const lobbies = createTable("lobby", (d) => ({
  id: d
    .varchar({ length: 255 })
    .primaryKey()
    .notNull()
    .$defaultFn(() => createId()),
  name: d.varchar({ length: 255 }).notNull(),
  maxPlayers: d.integer().notNull().default(0),
  createdById: d
    .varchar({ length: 255 })
    .notNull()
    .references(() => players.id, { onDelete: "cascade" }),
  createdAt: defaultTimeStamp("created_at", d),
  updatedAt: d
    .timestamp("updated_at", { withTimezone: true })
    .$onUpdate(() => new Date()),
}));

export type Lobby = typeof lobbies.$inferSelect & {
  members?: LobbyMember[];
  leader?: Player;
};

export const lobbyRelations = relations(lobbies, ({ many, one }) => ({
  leader: one(players, {
    fields: [lobbies.createdById],
    references: [players.id],
  }),
  members: many(lobbyMembers),
}));

export const lobbyMembers = createTable(
  "lobby_member",
  (d) => ({
    playerId: d
      .varchar({ length: 255 })
      .notNull()
      .references(() => players.id, { onDelete: "cascade" }),
    lobbyId: d
      .varchar({ length: 255 })
      .notNull()
      .references(() => lobbies.id, { onDelete: "cascade" }),
    joinedAt: defaultTimeStamp("joined_at", d),
    role: d
      .varchar({ length: 255 })
      .notNull()
      .$type<LobbyMemberRole>()
      .default("player"),
    isReady: d.boolean().notNull(),
  }),
  (t) => [primaryKey({ columns: [t.playerId] })],
);

export type LobbyMember = typeof lobbyMembers.$inferSelect & {
  player?: Player;
};

export const lobbyMembersRelations = relations(lobbyMembers, ({ one }) => ({
  lobby: one(lobbies, {
    fields: [lobbyMembers.lobbyId],
    references: [lobbies.id],
  }),
  player: one(players, {
    fields: [lobbyMembers.playerId],
    references: [players.id],
  }),
}));

export const players = createTable(
  "player",
  (d) => ({
    id: d
      .varchar({ length: 255 })
      .notNull()
      .references(() => users.id, { onDelete: "cascade" }),
    displayName: d.varchar({ length: 255 }),
    avatar: d.varchar({ length: 255 }),
    joinedAt: defaultTimeStamp("joined_at", d),
  }),
  (t) => [
    primaryKey({
      columns: [t.id],
    }),
  ],
);
export type Player = typeof players.$inferSelect;

export const users = createTable("user", (d) => ({
  id: d
    .varchar({ length: 255 })
    .notNull()
    .primaryKey()
    .$defaultFn(() => createId()),
  name: d.varchar({ length: 255 }),
  email: d.varchar({ length: 255 }).notNull(),
  emailVerified: d
    .timestamp("email_verified", {
      mode: "date",
      withTimezone: true,
    })
    .default(sql`CURRENT_TIMESTAMP`),
}));

export const usersRelations = relations(users, ({ many }) => ({
  accounts: many(accounts),
  sessions: many(sessions),
}));

export const accounts = createTable(
  "account",
  (d) => ({
    userId: d
      .varchar({ length: 255 })
      .notNull()
      .references(() => users.id, { onDelete: "cascade" }),
    type: d.varchar({ length: 255 }).$type<AdapterAccount["type"]>().notNull(),
    provider: d.varchar({ length: 255 }).notNull(),
    providerAccountId: d.varchar({ length: 255 }).notNull(),
    refresh_token: d.text(),
    access_token: d.text(),
    expires_at: d.integer(),
    token_type: d.varchar({ length: 255 }),
    scope: d.varchar({ length: 255 }),
    id_token: d.text(),
    session_state: d.varchar({ length: 255 }),
  }),
  (t) => [
    primaryKey({
      columns: [t.provider, t.providerAccountId],
    }),
    index("account_user_id_idx").on(t.userId),
  ],
);

export const accountsRelations = relations(accounts, ({ one }) => ({
  user: one(users, { fields: [accounts.userId], references: [users.id] }),
}));

export const sessions = createTable(
  "session",
  (d) => ({
    sessionToken: d.varchar({ length: 255 }).notNull().primaryKey(),
    userId: d
      .varchar({ length: 255 })
      .notNull()
      .references(() => users.id, { onDelete: "cascade" }),
    expires: d.timestamp({ mode: "date" }).notNull(),
  }),
  (t) => [index("session_user_id_idx").on(t.userId)],
);

export const sessionsRelations = relations(sessions, ({ one }) => ({
  user: one(users, { fields: [sessions.userId], references: [users.id] }),
}));

export const verificationTokens = createTable(
  "verification_token",
  (d) => ({
    identifier: d.varchar({ length: 255 }).notNull(),
    token: d.varchar({ length: 255 }).notNull(),
    expires: d.timestamp({ mode: "date" }).notNull(),
  }),
  (t) => [primaryKey({ columns: [t.identifier, t.token] })],
);
