import type { Prisma } from "@capakraken/db";
import { EstimateVersionStatus, type UpdateEstimateDraftInput } from "@capakraken/shared";
import {
  buildProjectSnapshot,
  ESTIMATE_DETAIL_INCLUDE,
  type EstimateDbClient,
  type EstimateWithDetails,
} from "./shared.js";

function toAssumptionCreateInput(
  assumption: UpdateEstimateDraftInput["assumptions"][number],
) {
  return {
    ...(assumption.id !== undefined ? { id: assumption.id } : {}),
    category: assumption.category,
    key: assumption.key,
    label: assumption.label,
    valueType: assumption.valueType,
    value: assumption.value as Prisma.InputJsonValue,
    sortOrder: assumption.sortOrder,
    ...(assumption.notes !== undefined ? { notes: assumption.notes } : {}),
  };
}

function toScopeItemCreateInput(
  scopeItem: UpdateEstimateDraftInput["scopeItems"][number],
) {
  return {
    ...(scopeItem.id !== undefined ? { id: scopeItem.id } : {}),
    sequenceNo: scopeItem.sequenceNo,
    scopeType: scopeItem.scopeType,
    ...(scopeItem.packageCode !== undefined
      ? { packageCode: scopeItem.packageCode }
      : {}),
    name: scopeItem.name,
    ...(scopeItem.description !== undefined
      ? { description: scopeItem.description }
      : {}),
    ...(scopeItem.scene !== undefined ? { scene: scopeItem.scene } : {}),
    ...(scopeItem.page !== undefined ? { page: scopeItem.page } : {}),
    ...(scopeItem.location !== undefined ? { location: scopeItem.location } : {}),
    ...(scopeItem.assumptionCategory !== undefined
      ? { assumptionCategory: scopeItem.assumptionCategory }
      : {}),
    technicalSpec: scopeItem.technicalSpec as Prisma.InputJsonValue,
    ...(scopeItem.frameCount !== undefined
      ? { frameCount: scopeItem.frameCount }
      : {}),
    ...(scopeItem.itemCount !== undefined ? { itemCount: scopeItem.itemCount } : {}),
    ...(scopeItem.unitMode !== undefined ? { unitMode: scopeItem.unitMode } : {}),
    ...(scopeItem.internalComments !== undefined
      ? { internalComments: scopeItem.internalComments }
      : {}),
    ...(scopeItem.externalComments !== undefined
      ? { externalComments: scopeItem.externalComments }
      : {}),
    sortOrder: scopeItem.sortOrder,
    metadata: scopeItem.metadata as Prisma.InputJsonValue,
  };
}

function toDemandLineCreateInput(
  line: UpdateEstimateDraftInput["demandLines"][number],
) {
  return {
    ...(line.id !== undefined ? { id: line.id } : {}),
    ...(line.scopeItemId !== undefined ? { scopeItemId: line.scopeItemId } : {}),
    ...(line.roleId !== undefined ? { roleId: line.roleId } : {}),
    ...(line.resourceId !== undefined ? { resourceId: line.resourceId } : {}),
    lineType: line.lineType,
    name: line.name,
    ...(line.chapter !== undefined ? { chapter: line.chapter } : {}),
    hours: line.hours,
    ...(line.days !== undefined ? { days: line.days } : {}),
    ...(line.fte !== undefined ? { fte: line.fte } : {}),
    ...(line.rateSource !== undefined ? { rateSource: line.rateSource } : {}),
    costRateCents: line.costRateCents,
    billRateCents: line.billRateCents,
    currency: line.currency,
    costTotalCents: line.costTotalCents,
    priceTotalCents: line.priceTotalCents,
    monthlySpread: line.monthlySpread as Prisma.InputJsonValue,
    staffingAttributes: line.staffingAttributes as Prisma.InputJsonValue,
    metadata: line.metadata as Prisma.InputJsonValue,
  };
}

function toResourceSnapshotCreateInput(
  snapshot: UpdateEstimateDraftInput["resourceSnapshots"][number],
) {
  return {
    ...(snapshot.id !== undefined ? { id: snapshot.id } : {}),
    ...(snapshot.resourceId !== undefined ? { resourceId: snapshot.resourceId } : {}),
    ...(snapshot.sourceEid !== undefined ? { sourceEid: snapshot.sourceEid } : {}),
    displayName: snapshot.displayName,
    ...(snapshot.chapter !== undefined ? { chapter: snapshot.chapter } : {}),
    ...(snapshot.roleId !== undefined ? { roleId: snapshot.roleId } : {}),
    currency: snapshot.currency,
    lcrCents: snapshot.lcrCents,
    ucrCents: snapshot.ucrCents,
    ...(snapshot.fte !== undefined ? { fte: snapshot.fte } : {}),
    ...(snapshot.location !== undefined ? { location: snapshot.location } : {}),
    ...(snapshot.country !== undefined ? { country: snapshot.country } : {}),
    ...(snapshot.level !== undefined ? { level: snapshot.level } : {}),
    ...(snapshot.workType !== undefined ? { workType: snapshot.workType } : {}),
    attributes: snapshot.attributes as Prisma.InputJsonValue,
  };
}

function toMetricCreateInput(metric: UpdateEstimateDraftInput["metrics"][number]) {
  return {
    ...(metric.id !== undefined ? { id: metric.id } : {}),
    key: metric.key,
    label: metric.label,
    ...(metric.metricGroup !== undefined ? { metricGroup: metric.metricGroup } : {}),
    valueDecimal: metric.valueDecimal,
    ...(metric.valueCents !== undefined ? { valueCents: metric.valueCents } : {}),
    ...(metric.currency !== undefined ? { currency: metric.currency } : {}),
    metadata: metric.metadata as Prisma.InputJsonValue,
  };
}

export async function updateEstimateDraft(
  db: EstimateDbClient,
  input: UpdateEstimateDraftInput,
): Promise<EstimateWithDetails> {
  const existing = await db.estimate.findUnique({
    where: { id: input.id },
    include: ESTIMATE_DETAIL_INCLUDE,
  });

  if (!existing) {
    throw new Error("Estimate not found");
  }

  const workingVersion = existing.versions.find(
    (version) => version.status === EstimateVersionStatus.WORKING,
  );

  if (!workingVersion) {
    throw new Error("Estimate has no working version");
  }

  const effectiveProjectId = input.projectId ?? existing.projectId;
  const projectSnapshot = await buildProjectSnapshot(db, effectiveProjectId);

  await db.$transaction(async (tx) => {
    await tx.estimate.update({
      where: { id: input.id },
      data: {
        ...(input.projectId !== undefined ? { projectId: input.projectId } : {}),
        ...(input.name !== undefined ? { name: input.name } : {}),
        ...(input.opportunityId !== undefined
          ? { opportunityId: input.opportunityId }
          : {}),
        ...(input.baseCurrency !== undefined
          ? { baseCurrency: input.baseCurrency }
          : {}),
        ...(input.status !== undefined ? { status: input.status } : {}),
      },
    });

    await tx.estimateVersion.update({
      where: { id: workingVersion.id },
      data: {
        ...(input.versionLabel !== undefined ? { label: input.versionLabel } : {}),
        ...(input.versionNotes !== undefined ? { notes: input.versionNotes } : {}),
        projectSnapshot,
        assumptions: {
          deleteMany: {},
          create: input.assumptions.map(toAssumptionCreateInput),
        },
        scopeItems: {
          deleteMany: {},
          create: input.scopeItems.map(toScopeItemCreateInput),
        },
        demandLines: {
          deleteMany: {},
          create: input.demandLines.map(toDemandLineCreateInput),
        },
        resourceSnapshots: {
          deleteMany: {},
          create: input.resourceSnapshots.map(toResourceSnapshotCreateInput),
        },
        metrics: {
          deleteMany: {},
          create: input.metrics.map(toMetricCreateInput),
        },
      },
    });
  });

  const updated = await db.estimate.findUnique({
    where: { id: input.id },
    include: ESTIMATE_DETAIL_INCLUDE,
  });

  if (!updated) {
    throw new Error("Estimate not found after update");
  }

  return updated;
}
