import type {
  EstimateDemandLine,
  EstimateDemandLineCalculationMetadata,
  EstimateDemandLineRateMode,
  EstimateDemandSummary,
} from "@capakraken/shared";

export interface EstimateDemandLineRateSnapshot {
  resourceId?: string | null;
  currency?: string | null;
  lcrCents: number;
  ucrCents: number;
}

export interface EstimateDemandLineForCalculation {
  resourceId?: string | null | undefined;
  hours: number;
  rateSource?: string | null | undefined;
  costRateCents: number;
  billRateCents: number;
  currency?: string | null | undefined;
  costTotalCents: number;
  priceTotalCents: number;
  metadata?: Record<string, unknown> | null | undefined;
}

type ParsedDemandLineMetadata = Record<string, unknown> & {
  calculation?: Partial<EstimateDemandLineCalculationMetadata>;
};

function parseRateMode(value: unknown): EstimateDemandLineRateMode | undefined {
  return value === "resource" || value === "manual" ? value : undefined;
}

function parseDemandLineMetadata(
  metadata: Record<string, unknown> | null | undefined,
): ParsedDemandLineMetadata {
  const safeMetadata =
    typeof metadata === "object" && metadata !== null && !Array.isArray(metadata)
      ? metadata
      : {};
  const rawCalculation =
    typeof safeMetadata.calculation === "object" &&
    safeMetadata.calculation !== null &&
    !Array.isArray(safeMetadata.calculation)
      ? (safeMetadata.calculation as Record<string, unknown>)
      : undefined;

  if (!rawCalculation) {
    return safeMetadata as ParsedDemandLineMetadata;
  }

  const calculation: Partial<EstimateDemandLineCalculationMetadata> = {};

  const costRateMode = parseRateMode(rawCalculation.costRateMode);
  if (costRateMode) {
    calculation.costRateMode = costRateMode;
  }

  const billRateMode = parseRateMode(rawCalculation.billRateMode);
  if (billRateMode) {
    calculation.billRateMode = billRateMode;
  }

  if (rawCalculation.totalMode === "computed") {
    calculation.totalMode = "computed";
  }

  if (typeof rawCalculation.liveCostRateCents === "number") {
    calculation.liveCostRateCents = rawCalculation.liveCostRateCents;
  }

  if (typeof rawCalculation.liveBillRateCents === "number") {
    calculation.liveBillRateCents = rawCalculation.liveBillRateCents;
  }

  if (typeof rawCalculation.liveCurrency === "string") {
    calculation.liveCurrency = rawCalculation.liveCurrency;
  }

  return {
    ...safeMetadata,
    ...(Object.keys(calculation).length > 0 ? { calculation } : {}),
  };
}

function inferRateMode(
  resourceSnapshot: EstimateDemandLineRateSnapshot | null | undefined,
  effectiveRateCents: number,
  liveRateCents: number | undefined,
  explicitMode: EstimateDemandLineRateMode | undefined,
): EstimateDemandLineRateMode {
  if (explicitMode) {
    return explicitMode;
  }

  if (!resourceSnapshot || liveRateCents == null) {
    return "manual";
  }

  return effectiveRateCents === liveRateCents ? "resource" : "manual";
}

export function getEstimateDemandLineCalculationMetadata(
  line: Pick<
    EstimateDemandLineForCalculation,
    "resourceId" | "costRateCents" | "billRateCents" | "metadata"
  >,
  options?: {
    resourceSnapshot?: EstimateDemandLineRateSnapshot | null | undefined;
  },
): EstimateDemandLineCalculationMetadata {
  const parsedMetadata = parseDemandLineMetadata(line.metadata);
  const explicitCalculation = parsedMetadata.calculation;
  const resourceSnapshot = options?.resourceSnapshot;
  const liveCostRateCents = resourceSnapshot?.lcrCents;
  const liveBillRateCents = resourceSnapshot?.ucrCents;

  return {
    costRateMode: inferRateMode(
      resourceSnapshot,
      line.costRateCents,
      liveCostRateCents,
      explicitCalculation?.costRateMode,
    ),
    billRateMode: inferRateMode(
      resourceSnapshot,
      line.billRateCents,
      liveBillRateCents,
      explicitCalculation?.billRateMode,
    ),
    totalMode: "computed",
    liveCostRateCents: liveCostRateCents ?? null,
    liveBillRateCents: liveBillRateCents ?? null,
    liveCurrency: resourceSnapshot?.currency ?? null,
  };
}

export function normalizeEstimateDemandLine<T extends EstimateDemandLineForCalculation>(
  line: T,
  options?: {
    resourceSnapshot?: EstimateDemandLineRateSnapshot | null | undefined;
    defaultCurrency?: string;
  },
): T {
  const resourceSnapshot = options?.resourceSnapshot;
  const calculation = getEstimateDemandLineCalculationMetadata(line, {
    resourceSnapshot,
  });
  const effectiveCostRateCents =
    calculation.costRateMode === "resource" && resourceSnapshot
      ? resourceSnapshot.lcrCents
      : line.costRateCents;
  const effectiveBillRateCents =
    calculation.billRateMode === "resource" && resourceSnapshot
      ? resourceSnapshot.ucrCents
      : line.billRateCents;
  const currency =
    ((calculation.costRateMode === "resource" ||
      calculation.billRateMode === "resource") &&
    resourceSnapshot?.currency
      ? resourceSnapshot.currency
      : line.currency) ||
    resourceSnapshot?.currency ||
    options?.defaultCurrency ||
    "EUR";
  const metadata = parseDemandLineMetadata(line.metadata);

  return {
    ...line,
    costRateCents: effectiveCostRateCents,
    billRateCents: effectiveBillRateCents,
    currency,
    costTotalCents: Math.round(line.hours * effectiveCostRateCents),
    priceTotalCents: Math.round(line.hours * effectiveBillRateCents),
    metadata: {
      ...metadata,
      calculation,
    },
  };
}

export function summarizeEstimateDemandLines(
  demandLines: Pick<
    EstimateDemandLine,
    "hours" | "costTotalCents" | "priceTotalCents"
  >[],
): EstimateDemandSummary {
  const totalHours = demandLines.reduce((sum, line) => sum + line.hours, 0);
  const totalCostCents = demandLines.reduce(
    (sum, line) => sum + line.costTotalCents,
    0,
  );
  const totalPriceCents = demandLines.reduce(
    (sum, line) => sum + line.priceTotalCents,
    0,
  );
  const marginCents = totalPriceCents - totalCostCents;
  const marginPercent =
    totalPriceCents > 0 ? Math.round((marginCents / totalPriceCents) * 100) : 0;

  return {
    totalHours,
    totalCostCents,
    totalPriceCents,
    marginCents,
    marginPercent,
  };
}
