import {
  CreateRateCardLineSchema,
  CreateRateCardSchema,
  UpdateRateCardLineSchema,
  UpdateRateCardSchema,
} from "@capakraken/shared";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { createTRPCRouter, controllerProcedure, managerProcedure } from "../trpc.js";
import { createAuditEntry } from "../lib/audit.js";
import {
  lookupBestRateMatch,
  rateCardLineSelect,
  resolveBestRate,
  resolveBestRateLineMatch,
} from "./rate-card-read.js";
import {
  buildRateCardCreateAuditAfter,
  buildRateCardCreateData,
  buildRateCardLineCreateAuditAfter,
  buildRateCardLineCreateData,
  buildRateCardLineUpdateData,
  buildRateCardListWhere,
  buildRateCardReplaceLinesAuditAfter,
  buildRateCardUpdateData,
  rateCardCreateInclude,
  rateCardDetailInclude,
  buildRateCardNestedLineCreateData,
  rateCardSummaryInclude,
} from "./rate-card-write-support.js";

export const rateCardRouter = createTRPCRouter({
  list: controllerProcedure
    .input(
      z.object({
        isActive: z.boolean().optional(),
        search: z.string().optional(),
        clientId: z.string().optional(),
        effectiveAt: z.coerce.date().optional(),
      }).optional(),
    )
    .query(async ({ ctx, input }) => {
      return ctx.db.rateCard.findMany({
        where: buildRateCardListWhere(input),
        include: rateCardSummaryInclude,
        orderBy: [{ isActive: "desc" }, { effectiveFrom: "desc" }, { name: "asc" }],
      });
    }),

  getById: controllerProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const rateCard = await findUniqueOrThrow(
        ctx.db.rateCard.findUnique({
          where: { id: input.id },
          include: rateCardDetailInclude,
        }),
        "Rate card",
      );
      return rateCard;
    }),

  lookupBestMatch: controllerProcedure
    .input(z.object({
      clientId: z.string().optional(),
      chapter: z.string().optional(),
      managementLevelId: z.string().optional(),
      roleName: z.string().optional(),
      seniority: z.string().optional(),
    }))
    .query(async ({ ctx, input }) => lookupBestRateMatch(ctx.db, input)),

  resolveBestRate: controllerProcedure
    .input(z.object({
      resourceId: z.string().optional(),
      roleName: z.string().optional(),
      date: z.coerce.date().optional(),
    }))
    .query(async ({ ctx, input }) => resolveBestRate(ctx.db, input)),

  create: managerProcedure
    .input(CreateRateCardSchema)
    .mutation(async ({ ctx, input }) => {
      const { lines, name, currency } = input;

      const rateCard = await ctx.db.rateCard.create({
        data: buildRateCardCreateData(input),
        include: rateCardCreateInclude,
      });

      void createAuditEntry({
        db: ctx.db,
        entityType: "RateCard",
        entityId: rateCard.id,
        entityName: rateCard.name,
        action: "CREATE",
        userId: ctx.dbUser?.id,
        after: buildRateCardCreateAuditAfter({ name, currency, lines }),
        source: "ui",
      });

      return rateCard;
    }),

  update: managerProcedure
    .input(z.object({ id: z.string(), data: UpdateRateCardSchema }))
    .mutation(async ({ ctx, input }) => {
      const before = await findUniqueOrThrow(
        ctx.db.rateCard.findUnique({ where: { id: input.id } }),
        "Rate card",
      );

      const updated = await ctx.db.rateCard.update({
        where: { id: input.id },
        data: buildRateCardUpdateData(input.data),
        include: rateCardSummaryInclude,
      });

      void createAuditEntry({
        db: ctx.db,
        entityType: "RateCard",
        entityId: input.id,
        entityName: updated.name,
        action: "UPDATE",
        userId: ctx.dbUser?.id,
        before: before as unknown as Record<string, unknown>,
        after: updated as unknown as Record<string, unknown>,
        source: "ui",
      });

      return updated;
    }),

  deactivate: managerProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const deactivated = await ctx.db.rateCard.update({
        where: { id: input.id },
        data: { isActive: false },
      });

      void createAuditEntry({
        db: ctx.db,
        entityType: "RateCard",
        entityId: input.id,
        entityName: deactivated.name,
        action: "DELETE",
        userId: ctx.dbUser?.id,
        source: "ui",
        summary: "Deactivated rate card",
      });

      return deactivated;
    }),

  // ─── Line CRUD ─────────────────────────────────────────────────────────────

  addLine: managerProcedure
    .input(z.object({ rateCardId: z.string(), line: CreateRateCardLineSchema }))
    .mutation(async ({ ctx, input }) => {
      const rateCard = await findUniqueOrThrow(
        ctx.db.rateCard.findUnique({ where: { id: input.rateCardId } }),
        "Rate card",
      );

      const line = await ctx.db.rateCardLine.create({
        data: buildRateCardLineCreateData(input.rateCardId, input.line),
        select: rateCardLineSelect,
      });

      void createAuditEntry({
        db: ctx.db,
        entityType: "RateCardLine",
        entityId: line.id,
        entityName: `${rateCard.name} — ${input.line.chapter ?? "line"}`,
        action: "CREATE",
        userId: ctx.dbUser?.id,
        after: buildRateCardLineCreateAuditAfter({
          rateCardId: input.rateCardId,
          line: input.line,
        }),
        source: "ui",
      });

      return line;
    }),

  updateLine: managerProcedure
    .input(z.object({ lineId: z.string(), data: UpdateRateCardLineSchema }))
    .mutation(async ({ ctx, input }) => {
      const before = await findUniqueOrThrow(
        ctx.db.rateCardLine.findUnique({ where: { id: input.lineId } }),
        "Rate card line",
      );

      const updated = await ctx.db.rateCardLine.update({
        where: { id: input.lineId },
        data: buildRateCardLineUpdateData(input.data),
        select: rateCardLineSelect,
      });

      void createAuditEntry({
        db: ctx.db,
        entityType: "RateCardLine",
        entityId: input.lineId,
        action: "UPDATE",
        userId: ctx.dbUser?.id,
        before: before as unknown as Record<string, unknown>,
        after: updated as unknown as Record<string, unknown>,
        source: "ui",
      });

      return updated;
    }),

  deleteLine: managerProcedure
    .input(z.object({ lineId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const line = await findUniqueOrThrow(
        ctx.db.rateCardLine.findUnique({ where: { id: input.lineId } }),
        "Rate card line",
      );

      await ctx.db.rateCardLine.delete({ where: { id: input.lineId } });

      void createAuditEntry({
        db: ctx.db,
        entityType: "RateCardLine",
        entityId: input.lineId,
        action: "DELETE",
        userId: ctx.dbUser?.id,
        before: line as unknown as Record<string, unknown>,
        source: "ui",
      });

      return { deleted: true };
    }),

  // ─── Batch operations ──────────────────────────────────────────────────────

  replaceLines: managerProcedure
    .input(z.object({
      rateCardId: z.string(),
      lines: z.array(CreateRateCardLineSchema),
    }))
    .mutation(async ({ ctx, input }) => {
      const rateCard = await findUniqueOrThrow(
        ctx.db.rateCard.findUnique({ where: { id: input.rateCardId } }),
        "Rate card",
      );

      const result = await ctx.db.$transaction(async (tx) => {
        await tx.rateCardLine.deleteMany({ where: { rateCardId: input.rateCardId } });

        const created = await Promise.all(
          input.lines.map((line) =>
            tx.rateCardLine.create({
              data: {
                rateCardId: input.rateCardId,
                ...buildRateCardNestedLineCreateData(line),
              },
              select: rateCardLineSelect,
            }),
          ),
        );

        return created;
      });

      void createAuditEntry({
        db: ctx.db,
        entityType: "RateCard",
        entityId: input.rateCardId,
        entityName: rateCard.name,
        action: "UPDATE",
        userId: ctx.dbUser?.id,
        after: buildRateCardReplaceLinesAuditAfter(result.length),
        source: "ui",
        summary: `Replaced all lines with ${result.length} new lines`,
      });

      return result;
    }),

  // ─── Rate resolution ───────────────────────────────────────────────────────

  resolveRateLine: controllerProcedure
    .input(z.object({
      rateCardId: z.string(),
      roleId: z.string().optional(),
      chapter: z.string().optional(),
      location: z.string().optional(),
      seniority: z.string().optional(),
      workType: z.string().optional(),
    }))
    .query(async ({ ctx, input }) => {
      const { rateCardId, ...criteria } = input;

      // Find the most specific matching line (most criteria matched wins)
      const lines = await ctx.db.rateCardLine.findMany({
        where: { rateCardId },
        select: rateCardLineSelect,
      });
      return resolveBestRateLineMatch(lines, criteria);
    }),
});
