import { getEstimateById, listEstimates } from "@capakraken/application";
import { summarizeEstimateDemandLines } from "@capakraken/engine";
import { EstimateListFiltersSchema } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { controllerProcedure } from "../trpc.js";

async function readEstimateVersionSnapshot(
  db: Parameters<typeof getEstimateById>[0],
  input: { estimateId: string; versionId?: string | undefined },
) {
  const estimate = await db.estimate.findUnique({
    where: { id: input.estimateId },
    select: {
      id: true,
      name: true,
      status: true,
      baseCurrency: true,
      versions: {
        ...(input.versionId
          ? { where: { id: input.versionId } }
          : { orderBy: { versionNumber: "desc" as const }, take: 1 }),
        select: {
          id: true,
          versionNumber: true,
          label: true,
          status: true,
          notes: true,
          lockedAt: true,
          createdAt: true,
          updatedAt: true,
          assumptions: {
            select: { id: true, category: true, key: true, label: true },
          },
          scopeItems: {
            select: { id: true, scopeType: true, sequenceNo: true, name: true },
            orderBy: [{ sequenceNo: "asc" }, { name: "asc" }],
          },
          demandLines: {
            select: {
              id: true,
              name: true,
              chapter: true,
              hours: true,
              costTotalCents: true,
              priceTotalCents: true,
              currency: true,
            },
          },
          resourceSnapshots: {
            select: {
              id: true,
              displayName: true,
              chapter: true,
              currency: true,
              lcrCents: true,
              ucrCents: true,
            },
          },
          exports: {
            select: {
              id: true,
              format: true,
              fileName: true,
              createdAt: true,
            },
            orderBy: { createdAt: "desc" },
          },
        },
      },
    },
  });

  if (!estimate || estimate.versions.length === 0) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Estimate version not found" });
  }

  const version = estimate.versions[0]!;
  const demandSummary = summarizeEstimateDemandLines(version.demandLines);

  const chapterTotals = version.demandLines.reduce<Record<string, {
    lineCount: number;
    hours: number;
    costTotalCents: number;
    priceTotalCents: number;
    currency: string;
  }>>((acc, line) => {
    const key = line.chapter ?? "Unassigned";
    const current = acc[key] ?? {
      lineCount: 0,
      hours: 0,
      costTotalCents: 0,
      priceTotalCents: 0,
      currency: line.currency,
    };
    current.lineCount += 1;
    current.hours += line.hours;
    current.costTotalCents += line.costTotalCents;
    current.priceTotalCents += line.priceTotalCents;
    acc[key] = current;
    return acc;
  }, {});

  const scopeTypeTotals = version.scopeItems.reduce<Record<string, number>>((acc, item) => {
    acc[item.scopeType] = (acc[item.scopeType] ?? 0) + 1;
    return acc;
  }, {});

  const assumptionCategoryTotals = version.assumptions.reduce<Record<string, number>>((acc, assumption) => {
    acc[assumption.category] = (acc[assumption.category] ?? 0) + 1;
    return acc;
  }, {});

  return {
    estimate: {
      id: estimate.id,
      name: estimate.name,
      status: estimate.status,
      baseCurrency: estimate.baseCurrency,
    },
    version: {
      id: version.id,
      versionNumber: version.versionNumber,
      label: version.label,
      status: version.status,
      notes: version.notes,
      lockedAt: version.lockedAt,
      createdAt: version.createdAt,
      updatedAt: version.updatedAt,
    },
    counts: {
      assumptions: version.assumptions.length,
      scopeItems: version.scopeItems.length,
      demandLines: version.demandLines.length,
      resourceSnapshots: version.resourceSnapshots.length,
      exports: version.exports.length,
    },
    totals: {
      hours: demandSummary.totalHours,
      costTotalCents: demandSummary.totalCostCents,
      priceTotalCents: demandSummary.totalPriceCents,
      marginCents: demandSummary.marginCents,
      marginPercent: demandSummary.marginPercent,
    },
    chapterBreakdown: Object.entries(chapterTotals)
      .sort((left, right) => right[1].hours - left[1].hours)
      .map(([chapter, totals]) => ({
        chapter,
        ...totals,
      })),
    scopeTypeBreakdown: Object.entries(scopeTypeTotals)
      .sort((left, right) => right[1] - left[1])
      .map(([scopeType, count]) => ({ scopeType, count })),
    assumptionCategoryBreakdown: Object.entries(assumptionCategoryTotals)
      .sort((left, right) => right[1] - left[1])
      .map(([category, count]) => ({ category, count })),
    exports: version.exports,
  };
}

export const estimateReadProcedures = {
  list: controllerProcedure
    .input(EstimateListFiltersSchema.default({}))
    .query(async ({ ctx, input }) =>
      listEstimates(
        ctx.db as unknown as Parameters<typeof listEstimates>[0],
        input,
      )),

  getById: controllerProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) =>
      findUniqueOrThrow(
        getEstimateById(
          ctx.db as unknown as Parameters<typeof getEstimateById>[0],
          input.id,
        ),
        "Estimate",
      )),

  listVersions: controllerProcedure
    .input(z.object({ estimateId: z.string() }))
    .query(async ({ ctx, input }) =>
      findUniqueOrThrow(
        ctx.db.estimate.findUnique({
          where: { id: input.estimateId },
          select: {
            id: true,
            name: true,
            status: true,
            latestVersionNumber: true,
            versions: {
              orderBy: { versionNumber: "desc" },
              select: {
                id: true,
                versionNumber: true,
                label: true,
                status: true,
                notes: true,
                lockedAt: true,
                createdAt: true,
                updatedAt: true,
                _count: {
                  select: {
                    assumptions: true,
                    scopeItems: true,
                    demandLines: true,
                    resourceSnapshots: true,
                    exports: true,
                  },
                },
              },
            },
          },
        }),
        "Estimate",
      )),

  getVersionSnapshot: controllerProcedure
    .input(z.object({ estimateId: z.string(), versionId: z.string().optional() }))
    .query(async ({ ctx, input }) => readEstimateVersionSnapshot(
      ctx.db as unknown as Parameters<typeof getEstimateById>[0],
      input,
    )),
};
