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

type RateCardProcedureContext = Pick<TRPCContext, "db" | "dbUser">;

function withAuditUser(userId: string | undefined) {
  return userId ? { userId } : {};
}

export const rateCardListInputSchema = z.object({
  isActive: z.boolean().optional(),
  search: z.string().optional(),
  clientId: z.string().optional(),
  effectiveAt: z.coerce.date().optional(),
}).optional();

export const rateCardByIdInputSchema = z.object({ id: z.string() });

export const rateCardLookupBestMatchInputSchema = z.object({
  clientId: z.string().optional(),
  chapter: z.string().optional(),
  managementLevelId: z.string().optional(),
  roleName: z.string().optional(),
  seniority: z.string().optional(),
});

export const rateCardResolveBestRateInputSchema = z.object({
  resourceId: z.string().optional(),
  roleName: z.string().optional(),
  date: z.coerce.date().optional(),
});

export const rateCardUpdateInputSchema = z.object({
  id: z.string(),
  data: UpdateRateCardSchema,
});

export const rateCardAddLineInputSchema = z.object({
  rateCardId: z.string(),
  line: CreateRateCardLineSchema,
});

export const rateCardUpdateLineInputSchema = z.object({
  lineId: z.string(),
  data: UpdateRateCardLineSchema,
});

export const rateCardDeleteLineInputSchema = z.object({ lineId: z.string() });

export const rateCardReplaceLinesInputSchema = z.object({
  rateCardId: z.string(),
  lines: z.array(CreateRateCardLineSchema),
});

export const rateCardResolveLineInputSchema = 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(),
});

type RateCardListInput = z.infer<typeof rateCardListInputSchema>;
type RateCardByIdInput = z.infer<typeof rateCardByIdInputSchema>;
type RateCardLookupBestMatchInput = z.infer<typeof rateCardLookupBestMatchInputSchema>;
type RateCardResolveBestRateInput = z.infer<typeof rateCardResolveBestRateInputSchema>;
type RateCardCreateInput = z.infer<typeof CreateRateCardSchema>;
type RateCardUpdateInput = z.infer<typeof rateCardUpdateInputSchema>;
type RateCardDeactivateInput = z.infer<typeof rateCardByIdInputSchema>;
type RateCardAddLineInput = z.infer<typeof rateCardAddLineInputSchema>;
type RateCardUpdateLineInput = z.infer<typeof rateCardUpdateLineInputSchema>;
type RateCardDeleteLineInput = z.infer<typeof rateCardDeleteLineInputSchema>;
type RateCardReplaceLinesInput = z.infer<typeof rateCardReplaceLinesInputSchema>;
type RateCardResolveLineInput = z.infer<typeof rateCardResolveLineInputSchema>;

export async function listRateCards(
  ctx: RateCardProcedureContext,
  input: RateCardListInput,
) {
  return ctx.db.rateCard.findMany({
    where: buildRateCardListWhere(input),
    include: rateCardSummaryInclude,
    orderBy: [{ isActive: "desc" }, { effectiveFrom: "desc" }, { name: "asc" }],
  });
}

export async function getRateCardById(
  ctx: RateCardProcedureContext,
  input: RateCardByIdInput,
) {
  return findUniqueOrThrow(
    ctx.db.rateCard.findUnique({
      where: { id: input.id },
      include: rateCardDetailInclude,
    }),
    "Rate card",
  );
}

export async function lookupRateCardBestMatch(
  ctx: RateCardProcedureContext,
  input: RateCardLookupBestMatchInput,
) {
  return lookupBestRateMatch(ctx.db, input);
}

export async function resolveRateCard(
  ctx: RateCardProcedureContext,
  input: RateCardResolveBestRateInput,
) {
  return resolveBestRate(ctx.db, input);
}

export async function createRateCard(
  ctx: RateCardProcedureContext,
  input: RateCardCreateInput,
) {
  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",
    ...withAuditUser(ctx.dbUser?.id),
    after: buildRateCardCreateAuditAfter({ name, currency, lines }),
    source: "ui",
  });

  return rateCard;
}

export async function updateRateCard(
  ctx: RateCardProcedureContext,
  input: RateCardUpdateInput,
) {
  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",
    ...withAuditUser(ctx.dbUser?.id),
    before: before as unknown as Record<string, unknown>,
    after: updated as unknown as Record<string, unknown>,
    source: "ui",
  });

  return updated;
}

export async function deactivateRateCard(
  ctx: RateCardProcedureContext,
  input: RateCardDeactivateInput,
) {
  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",
    ...withAuditUser(ctx.dbUser?.id),
    source: "ui",
    summary: "Deactivated rate card",
  });

  return deactivated;
}

export async function addRateCardLine(
  ctx: RateCardProcedureContext,
  input: RateCardAddLineInput,
) {
  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",
    ...withAuditUser(ctx.dbUser?.id),
    after: buildRateCardLineCreateAuditAfter({
      rateCardId: input.rateCardId,
      line: input.line,
    }),
    source: "ui",
  });

  return line;
}

export async function updateRateCardLine(
  ctx: RateCardProcedureContext,
  input: RateCardUpdateLineInput,
) {
  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",
    ...withAuditUser(ctx.dbUser?.id),
    before: before as unknown as Record<string, unknown>,
    after: updated as unknown as Record<string, unknown>,
    source: "ui",
  });

  return updated;
}

export async function deleteRateCardLine(
  ctx: RateCardProcedureContext,
  input: RateCardDeleteLineInput,
) {
  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",
    ...withAuditUser(ctx.dbUser?.id),
    before: line as unknown as Record<string, unknown>,
    source: "ui",
  });

  return { deleted: true };
}

export async function replaceRateCardLines(
  ctx: RateCardProcedureContext,
  input: RateCardReplaceLinesInput,
) {
  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 } });

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

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

  return result;
}

export async function resolveRateCardLine(
  ctx: RateCardProcedureContext,
  input: RateCardResolveLineInput,
) {
  const { rateCardId, ...criteria } = input;
  const lines = await ctx.db.rateCardLine.findMany({
    where: { rateCardId },
    select: rateCardLineSelect,
  });
  return resolveBestRateLineMatch(lines, criteria);
}
