/**
 * Drizzle schema — Socialex
 *
 * Conventions :
 * - PK : `id` UUID v4 généré côté DB (gen_random_uuid()).
 * - Timestamps : `createdAt` / `updatedAt` partout, `with timezone`.
 * - Enums : Postgres enum natifs (typés côté Drizzle).
 * - JSON dynamique (formData de chaque dossier) : `jsonb`.
 *
 * Better Auth crée ses propres tables : `user`, `session`, `account`,
 * `verification` (cf. lib/auth.ts). On ajoute notre champ `role` sur user.
 */

import {
  pgTable,
  pgEnum,
  text,
  varchar,
  integer,
  timestamp,
  boolean,
  jsonb,
  uuid,
  uniqueIndex,
  index,
} from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";

/* ─── Enums ──────────────────────────────────────────────────────────── */

export const roleEnum = pgEnum("role", ["admin", "client"]);

export const dossierStatusEnum = pgEnum("dossier_status", [
  "formulaire", // form soumis, en attente paiement (Stripe en cours / abandon)
  "en_attente_paiement", // virement attendu OU paiement échoué
  "acompte_paye", // 50% reçu via Stripe, traitement peut démarrer
  "en_cours", // travail en cours
  "solde_a_payer", // travail terminé, attend règlement du solde 50%
  "termine", // intégralement payé et clôturé
  "annule",
]);

export const documentStatusEnum = pgEnum("document_status", [
  "demande", // admin a demandé un doc, client n'a pas encore uploadé
  "uploade", // client a uploadé, admin doit valider
  "valide",
  "rejete",
]);

export const invoiceStatusEnum = pgEnum("invoice_status", [
  "brouillon",
  "envoyee",
  "payee",
  "annulee",
]);

export const notificationTypeEnum = pgEnum("notification_type", [
  "document_demande",
  "document_valide",
  "dossier_avancement",
  "facture_envoyee",
  "facture_payee",
  "general",
]);

/* ─── Better Auth tables ─────────────────────────────────────────────── */
/* Définies ici en miroir du schema Better Auth pour pouvoir y faire
   référence dans nos relations. Better Auth les crée via ses migrations. */

export const user = pgTable(
  "user",
  {
    id: text("id").primaryKey(),
    name: text("name").notNull(),
    email: text("email").notNull().unique(),
    emailVerified: boolean("email_verified").notNull().default(false),
    image: text("image"),
    // Notre extension : rôle
    role: roleEnum("role").notNull().default("client"),
    createdAt: timestamp("created_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
    updatedAt: timestamp("updated_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
  },
  (t) => ({
    emailIdx: uniqueIndex("user_email_idx").on(t.email),
  })
);

export const session = pgTable("session", {
  id: text("id").primaryKey(),
  userId: text("user_id")
    .notNull()
    .references(() => user.id, { onDelete: "cascade" }),
  token: text("token").notNull().unique(),
  expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
  ipAddress: text("ip_address"),
  userAgent: text("user_agent"),
  createdAt: timestamp("created_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
  updatedAt: timestamp("updated_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
});

export const account = pgTable("account", {
  id: text("id").primaryKey(),
  userId: text("user_id")
    .notNull()
    .references(() => user.id, { onDelete: "cascade" }),
  accountId: text("account_id").notNull(),
  providerId: text("provider_id").notNull(),
  password: text("password"),
  accessToken: text("access_token"),
  refreshToken: text("refresh_token"),
  idToken: text("id_token"),
  accessTokenExpiresAt: timestamp("access_token_expires_at", {
    withTimezone: true,
  }),
  refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
    withTimezone: true,
  }),
  scope: text("scope"),
  createdAt: timestamp("created_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
  updatedAt: timestamp("updated_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
});

export const verification = pgTable("verification", {
  id: text("id").primaryKey(),
  identifier: text("identifier").notNull(),
  value: text("value").notNull(),
  expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
  createdAt: timestamp("created_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
  updatedAt: timestamp("updated_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
});

/* ─── Form templates ────────────────────────────────────────────────────
   Un template par service (creation-de-societe, depot-de-marques…).
   Édité par l'admin, rendu par <DynamicForm /> côté public. */

export const formTemplate = pgTable("form_template", {
  id: uuid("id").primaryKey().defaultRandom(),
  /** Slug du service (cf. lib/services.ts). Unique. */
  serviceSlug: text("service_slug").notNull().unique(),
  /** Titre humain affiché en haut du formulaire (ex: "Création de société"). */
  title: text("title").notNull(),
  /** Sous-titre / consignes générales. */
  description: text("description"),
  /** Liste ordonnée de définitions de champs. Voir lib/forms/types.ts. */
  fields: jsonb("fields").notNull().default([]),
  /** Liste de documents demandés (label, description, required). */
  documentRequests: jsonb("document_requests").notNull().default([]),
  /** Si false, le formulaire n'est pas accessible publiquement
   *  (le bouton "Démarrer mon dossier" reste mais redirige vers /contact). */
  published: boolean("published").notNull().default(true),
  createdAt: timestamp("created_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
  updatedAt: timestamp("updated_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
});

/* ─── Domaine métier ─────────────────────────────────────────────────── */

/** Profil client étendu : 1:1 avec user (où role = 'client'). */
export const clientProfile = pgTable("client_profile", {
  id: uuid("id").primaryKey().defaultRandom(),
  userId: text("user_id")
    .notNull()
    .unique()
    .references(() => user.id, { onDelete: "cascade" }),
  company: text("company"),
  siret: varchar("siret", { length: 14 }),
  phone: text("phone"),
  addressStreet: text("address_street"),
  addressZip: text("address_zip"),
  addressCity: text("address_city"),
  addressCountry: text("address_country").default("FR"),
  notes: text("notes"), // notes internes admin
  createdAt: timestamp("created_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
  updatedAt: timestamp("updated_at", { withTimezone: true })
    .notNull()
    .defaultNow(),
});

/** Dossier : une demande de service (création société, dépôt comptes…). */
export const dossier = pgTable(
  "dossier",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    /** Numéro humain : "D-2025-0001". Auto-généré côté app. */
    reference: text("reference").notNull().unique(),
    clientId: text("client_id")
      .notNull()
      .references(() => user.id, { onDelete: "restrict" }),
    /** Slug du service (ex: "creation-de-societe"). Lié à lib/services.ts. */
    serviceSlug: text("service_slug").notNull(),
    /** Titre lisible (ex: "Création SAS Atelier Vendôme"). */
    title: text("title").notNull(),
    status: dossierStatusEnum("status").notNull().default("formulaire"),
    /** Données du formulaire spécifiques au service (typed côté app via Zod). */
    formData: jsonb("form_data").notNull().default({}),
    /** Notes internes admin. */
    internalNotes: text("internal_notes"),
    createdAt: timestamp("created_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
    updatedAt: timestamp("updated_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
    completedAt: timestamp("completed_at", { withTimezone: true }),
  },
  (t) => ({
    clientIdx: index("dossier_client_idx").on(t.clientId),
    statusIdx: index("dossier_status_idx").on(t.status),
    serviceIdx: index("dossier_service_idx").on(t.serviceSlug),
  })
);

/** Document : demandé par admin, uploadé par client (ou admin). */
export const document = pgTable(
  "document",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    dossierId: uuid("dossier_id")
      .notNull()
      .references(() => dossier.id, { onDelete: "cascade" }),
    /** Nom demandé (ex: "Justificatif de domicile"). */
    label: text("label").notNull(),
    /** Description / consignes (ex: "moins de 3 mois"). */
    description: text("description"),
    status: documentStatusEnum("status").notNull().default("demande"),
    /** Nom de fichier original côté client. Null tant que pas uploadé. */
    fileName: text("file_name"),
    /** Path relatif dans UPLOADS_DIR. Null tant que pas uploadé. */
    filePath: text("file_path"),
    fileSizeBytes: integer("file_size_bytes"),
    mimeType: text("mime_type"),
    requestedById: text("requested_by_id").references(() => user.id),
    requestedAt: timestamp("requested_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
    uploadedById: text("uploaded_by_id").references(() => user.id),
    uploadedAt: timestamp("uploaded_at", { withTimezone: true }),
    validatedById: text("validated_by_id").references(() => user.id),
    validatedAt: timestamp("validated_at", { withTimezone: true }),
    rejectionReason: text("rejection_reason"),
    createdAt: timestamp("created_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
    updatedAt: timestamp("updated_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
  },
  (t) => ({
    dossierIdx: index("document_dossier_idx").on(t.dossierId),
    statusIdx: index("document_status_idx").on(t.status),
  })
);

/** Facture : émise par admin, payée par client. */
export const invoice = pgTable(
  "invoice",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    /** Numéro facture humain : "F-2025-0001". Auto-incrémenté côté app. */
    number: text("number").notNull().unique(),
    /** Client lié en base. Peut être null pour les factures manuelles
     *  saisies à la main (clientInfo override utilisé à la place). */
    clientId: text("client_id").references(() => user.id, {
      onDelete: "restrict",
    }),
    dossierId: uuid("dossier_id").references(() => dossier.id, {
      onDelete: "set null",
    }),
    status: invoiceStatusEnum("status").notNull().default("brouillon"),
    /** Montant TTC en centimes (évite les floats). Pour les factures manuelles,
     *  il est recalculé à la sauvegarde depuis lineItems + vatRateBps. */
    amountCents: integer("amount_cents").notNull(),
    /** Description / libellé court (récap). Pour les factures manuelles,
     *  on stocke la liste détaillée dans `lineItems`. */
    description: text("description").notNull(),
    /** Path PDF généré (relatif à UPLOADS_DIR/invoices). */
    pdfPath: text("pdf_path"),
    issuedAt: timestamp("issued_at", { withTimezone: true }),
    dueAt: timestamp("due_at", { withTimezone: true }),
    paidAt: timestamp("paid_at", { withTimezone: true }),
    paymentMethod: text("payment_method"), // virement, CB, autre
    paymentReference: text("payment_reference"),
    /** Lignes détaillées (factures manuelles). Array de
     *  { description, quantity, unitPriceHtCents }. Vide pour les factures
     *  auto issues des dossiers (qui n'ont qu'un libellé global). */
    lineItems: jsonb("line_items").notNull().default([]),
    /** Override des infos émetteur (pour les factures manuelles).
     *  Si null : on utilise les coordonnées Socialex par défaut. */
    emitterInfo: jsonb("emitter_info"),
    /** Override des infos client (pour les factures manuelles).
     *  Si null : on lit depuis user/clientProfile via clientId. */
    clientInfo: jsonb("client_info"),
    /** Taux TVA en points de base (2000 = 20 %). Null = TVA non applicable
     *  (art. 293 B du CGI ou facture auto sans TVA). */
    vatRateBps: integer("vat_rate_bps"),
    /** True : facture créée manuellement par l'admin (avec lignes, TVA,
     *  override émetteur/client). False : facture auto issue d'un dossier. */
    isManual: boolean("is_manual").notNull().default(false),
    createdAt: timestamp("created_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
    updatedAt: timestamp("updated_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
  },
  (t) => ({
    clientIdx: index("invoice_client_idx").on(t.clientId),
    statusIdx: index("invoice_status_idx").on(t.status),
    dossierIdx: index("invoice_dossier_idx").on(t.dossierId),
  })
);

/** Structure d'une ligne de facture manuelle. */
export type InvoiceLineItem = {
  description: string;
  quantity: number;
  unitPriceHtCents: number;
};

/** Structure d'un override émetteur (facture manuelle). */
export type InvoiceEmitterInfo = {
  name: string;
  tagline?: string | null;
  email?: string | null;
  addressLines?: string[] | null;
  siret?: string | null;
};

/** Structure d'un override client (facture manuelle). */
export type InvoiceClientInfo = {
  name: string;
  company?: string | null;
  email?: string | null;
  addressStreet?: string | null;
  addressZip?: string | null;
  addressCity?: string | null;
  addressCountry?: string | null;
  siret?: string | null;
};

/** Notification in-app pour user (admin ou client). */
export const notification = pgTable(
  "notification",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    userId: text("user_id")
      .notNull()
      .references(() => user.id, { onDelete: "cascade" }),
    type: notificationTypeEnum("type").notNull(),
    title: text("title").notNull(),
    message: text("message").notNull(),
    /** Lien interne (ex: "/espace-client/dossiers/abc"). */
    link: text("link"),
    readAt: timestamp("read_at", { withTimezone: true }),
    /** ID de la ressource associée (dossier, facture, document) pour deep link. */
    resourceType: text("resource_type"),
    resourceId: text("resource_id"),
    createdAt: timestamp("created_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
  },
  (t) => ({
    userIdx: index("notif_user_idx").on(t.userId),
    unreadIdx: index("notif_unread_idx").on(t.userId, t.readAt),
  })
);

/** Activity log : historique d'actions sur les dossiers (audit + timeline UX). */
export const activityLog = pgTable(
  "activity_log",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    dossierId: uuid("dossier_id")
      .notNull()
      .references(() => dossier.id, { onDelete: "cascade" }),
    actorId: text("actor_id").references(() => user.id),
    /** Code d'action : "dossier_cree", "doc_demande", "facture_envoyee"… */
    action: text("action").notNull(),
    /** Détails (ex: { documentId, oldStatus, newStatus }). */
    payload: jsonb("payload").notNull().default({}),
    createdAt: timestamp("created_at", { withTimezone: true })
      .notNull()
      .defaultNow(),
  },
  (t) => ({
    dossierIdx: index("log_dossier_idx").on(t.dossierId),
  })
);

/* ─── Relations ──────────────────────────────────────────────────────── */

export const userRelations = relations(user, ({ one, many }) => ({
  clientProfile: one(clientProfile, {
    fields: [user.id],
    references: [clientProfile.userId],
  }),
  dossiers: many(dossier),
  invoices: many(invoice),
  notifications: many(notification),
}));

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

export const dossierRelations = relations(dossier, ({ one, many }) => ({
  client: one(user, { fields: [dossier.clientId], references: [user.id] }),
  documents: many(document),
  invoices: many(invoice),
  activityLog: many(activityLog),
}));

export const documentRelations = relations(document, ({ one }) => ({
  dossier: one(dossier, {
    fields: [document.dossierId],
    references: [dossier.id],
  }),
  requestedBy: one(user, {
    fields: [document.requestedById],
    references: [user.id],
    relationName: "documentRequester",
  }),
  uploadedBy: one(user, {
    fields: [document.uploadedById],
    references: [user.id],
    relationName: "documentUploader",
  }),
}));

export const invoiceRelations = relations(invoice, ({ one }) => ({
  client: one(user, { fields: [invoice.clientId], references: [user.id] }),
  dossier: one(dossier, {
    fields: [invoice.dossierId],
    references: [dossier.id],
  }),
}));

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

export const activityLogRelations = relations(activityLog, ({ one }) => ({
  dossier: one(dossier, {
    fields: [activityLog.dossierId],
    references: [dossier.id],
  }),
  actor: one(user, { fields: [activityLog.actorId], references: [user.id] }),
}));

/* ─── Types inférés ──────────────────────────────────────────────────── */

export type User = typeof user.$inferSelect;
export type ClientProfile = typeof clientProfile.$inferSelect;
export type Dossier = typeof dossier.$inferSelect;
export type DossierInsert = typeof dossier.$inferInsert;
export type Document = typeof document.$inferSelect;
export type Invoice = typeof invoice.$inferSelect;
export type Notification = typeof notification.$inferSelect;
export type ActivityLog = typeof activityLog.$inferSelect;
export type FormTemplate = typeof formTemplate.$inferSelect;
export type FormTemplateInsert = typeof formTemplate.$inferInsert;
