import { sql } from "drizzle-orm";
import { index, pgEnum, pgTable, text, timestamp, uniqueIndex } from "drizzle-orm/pg-core";
import { intId, intPk } from "./columns";
import { users, workspaces } from "./identity";
import { channels } from "./channels";
import { events } from "./calendar";

export const callKindEnum = pgEnum("call_kind", ["audio", "video", "connect"]);

/**
 * A call's lifecycle. `ringing` is the window in which invitees can still be
 * reached; it becomes `active` the moment a second person joins, and `missed`
 * rather than `ended` if it never did — the distinction is what the missed
 * tab on /calls is built from, so it has to be part of the row and not
 * re-derived from participant states on every read.
 */
export const callStatusEnum = pgEnum("call_status", ["ringing", "active", "ended", "missed"]);

/**
 * Per-person state, which is deliberately not the same thing as the call's
 * status: one invitee may have declined while another is still ringing and a
 * third is talking. `missed` here means "was still ringing when the call
 * ended", written by endCall rather than by the invitee's own client (which
 * by definition never showed up to write anything).
 */
export const callParticipantStateEnum = pgEnum("call_participant_state", [
  "ringing",
  "joined",
  "left",
  "declined",
  "missed",
]);

export const calls = pgTable(
  "calls",
  {
    id: intPk(),
    workspaceId: intId("workspace_id")
      .notNull()
      .references(() => workspaces.id),
    kind: callKindEnum("kind").notNull().default("audio"),
    status: callStatusEnum("status").notNull().default("ringing"),
    // Optional context, not a scoping mechanism — the same role channelId
    // plays on tasks and events. A call started from a DM or a room records
    // where it came from so history can say "in #design".
    channelId: intId("channel_id").references(() => channels.id),
    // Set when the call was started from a scheduled calendar event, so the
    // event and the call it produced can be shown as one thing.
    eventId: intId("event_id").references(() => events.id),
    title: text("title"),
    startedBy: intId("started_by")
      .notNull()
      .references(() => users.id),
    startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
    // First moment two people were on the call at once. Duration is measured
    // from here, not from startedAt — otherwise every call's length would
    // include however long the phone rang.
    answeredAt: timestamp("answered_at", { withTimezone: true }),
    endedAt: timestamp("ended_at", { withTimezone: true }),
    // LiveKit room name (`call-{workspaceId}-{callId}`). Null on pre-Phase-6
    // rows that never minted a token; the token endpoint fills it on first use.
    livekitRoom: text("livekit_room"),
    recordingStatus: text("recording_status").notNull().default("idle"),
    recordingEgressId: text("recording_egress_id"),
    recordingObjectKey: text("recording_object_key"),
    transcriptStatus: text("transcript_status").notNull().default("idle"),
    transcript: text("transcript"),
    summary: text("summary"),
  },
  (t) => [
    // Hot path for GET /workspaces/:id/calls — history is always newest-first.
    index("calls_workspace_started_idx").on(t.workspaceId, t.startedAt),
    // "What is live right now" for the active-calls strip.
    index("calls_workspace_status_idx").on(t.workspaceId, t.status),
    index("calls_channel_idx").on(t.channelId),
    // One live Connect per channel — join-or-start, never two huddles.
    uniqueIndex("calls_live_connect_channel_idx")
      .on(t.channelId)
      .where(sql`${t.kind} = 'connect' AND ${t.status} IN ('ringing','active') AND ${t.channelId} IS NOT NULL`),
  ],
);

export const callParticipants = pgTable(
  "call_participants",
  {
    callId: intId("call_id")
      .notNull()
      .references(() => calls.id),
    userId: intId("user_id")
      .notNull()
      .references(() => users.id),
    state: callParticipantStateEnum("state").notNull().default("ringing"),
    invitedAt: timestamp("invited_at", { withTimezone: true }).notNull().defaultNow(),
    joinedAt: timestamp("joined_at", { withTimezone: true }),
    leftAt: timestamp("left_at", { withTimezone: true }),
    // When this user last acknowledged the call in their history. Null on a
    // missed call is what makes the badge count non-zero; it is per-person
    // because a call missed by one invitee was answered by another.
    seenAt: timestamp("seen_at", { withTimezone: true }),
  },
  (t) => [
    uniqueIndex("call_participants_pk").on(t.callId, t.userId),
    // "My call history" and the unseen-missed count — the reverse lookup.
    index("call_participants_user_idx").on(t.userId, t.state),
  ],
);

/** Workspace PSTN numbers (Twilio inbound). One e164 maps to one tenant so
 * an inbound SIP invite can be routed without guessing. */
export const workspacePhoneNumbers = pgTable(
  "workspace_phone_numbers",
  {
    id: intPk(),
    workspaceId: intId("workspace_id")
      .notNull()
      .references(() => workspaces.id),
    e164: text("e164").notNull(),
    label: text("label"),
    createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    uniqueIndex("workspace_phone_numbers_e164_idx").on(t.e164),
    index("workspace_phone_numbers_workspace_idx").on(t.workspaceId),
  ],
);
