import type { Prisma } from "@capakraken/db";
import type { ExperienceMultiplierRule as EngineRule } from "@capakraken/engine";
import {
  CreateExperienceMultiplierSetSchema,
  UpdateExperienceMultiplierSetSchema,
} from "@capakraken/shared";
import { z } from "zod";

type CreateExperienceMultiplierSetInput = z.infer<typeof CreateExperienceMultiplierSetSchema>;
type UpdateExperienceMultiplierSetInput = z.infer<typeof UpdateExperienceMultiplierSetSchema>;
type ExperienceMultiplierRuleRowInput =
  | CreateExperienceMultiplierSetInput["rules"][number]
  | NonNullable<UpdateExperienceMultiplierSetInput["rules"]>[number];

type ExperienceMultiplierRuleRecord = {
  chapter: string | null;
  location: string | null;
  level: string | null;
  costMultiplier: number;
  billMultiplier: number;
  shoringRatio: number | null;
  additionalEffortRatio: number | null;
  description: string | null;
};

type DemandLineRecord = {
  id: string;
  name: string;
  chapter: string | null;
  costRateCents: number;
  billRateCents: number;
  hours: number;
  metadata: unknown;
  staffingAttributes: unknown;
};

type MultiplierSetRecord = {
  id: string;
  name: string;
};

type ExperienceMultiplierResultRecord = {
  adjustedCostRateCents: number;
  adjustedBillRateCents: number;
  adjustedHours: number;
  appliedRules: string[];
};

export const experienceMultiplierRuleInclude = {
  rules: { orderBy: { sortOrder: "asc" as const } },
} as const;

function buildExperienceMultiplierRuleRow(
  input: ExperienceMultiplierRuleRowInput,
  index: number,
) {
  return {
    ...(input.chapter ? { chapter: input.chapter } : {}),
    ...(input.location ? { location: input.location } : {}),
    ...(input.level ? { level: input.level } : {}),
    costMultiplier: input.costMultiplier,
    billMultiplier: input.billMultiplier,
    ...(input.shoringRatio !== undefined ? { shoringRatio: input.shoringRatio } : {}),
    ...(input.additionalEffortRatio !== undefined ? { additionalEffortRatio: input.additionalEffortRatio } : {}),
    ...(input.description ? { description: input.description } : {}),
    sortOrder: input.sortOrder ?? index,
  };
}

export function buildExperienceMultiplierNestedCreateRows(
  rules: ExperienceMultiplierRuleRowInput[],
): Prisma.ExperienceMultiplierRuleUncheckedCreateWithoutMultiplierSetInput[] {
  return rules.map((rule, index) => ({
    ...buildExperienceMultiplierRuleRow(rule, index),
  }));
}

export function buildExperienceMultiplierCreateManyRows(
  rules: ExperienceMultiplierRuleRowInput[],
  multiplierSetId: string,
): Prisma.ExperienceMultiplierRuleCreateManyInput[] {
  return rules.map((rule, index) => ({
    multiplierSetId,
    ...buildExperienceMultiplierRuleRow(rule, index),
  }));
}

export function buildExperienceMultiplierSetCreateData(
  input: CreateExperienceMultiplierSetInput,
): Prisma.ExperienceMultiplierSetCreateInput {
  return {
    name: input.name,
    ...(input.description ? { description: input.description } : {}),
    isDefault: input.isDefault,
    rules: {
      create: buildExperienceMultiplierNestedCreateRows(input.rules),
    },
  };
}

export function buildExperienceMultiplierSetUpdateData(
  input: Omit<UpdateExperienceMultiplierSetInput, "id" | "rules">,
): Prisma.ExperienceMultiplierSetUncheckedUpdateInput {
  return {
    ...(input.name !== undefined ? { name: input.name } : {}),
    ...(input.description !== undefined ? { description: input.description } : {}),
    ...(input.isDefault !== undefined ? { isDefault: input.isDefault } : {}),
  };
}

export function toExperienceMultiplierEngineRules(
  dbRules: ExperienceMultiplierRuleRecord[],
): EngineRule[] {
  return dbRules.map((rule) => ({
    ...(rule.chapter != null ? { chapter: rule.chapter } : {}),
    ...(rule.location != null ? { location: rule.location } : {}),
    ...(rule.level != null ? { level: rule.level } : {}),
    costMultiplier: rule.costMultiplier,
    billMultiplier: rule.billMultiplier,
    ...(rule.shoringRatio != null ? { shoringRatio: rule.shoringRatio } : {}),
    ...(rule.additionalEffortRatio != null ? { additionalEffortRatio: rule.additionalEffortRatio } : {}),
    ...(rule.description != null ? { description: rule.description } : {}),
  }));
}

export function buildExperienceMultiplierInput(
  line: DemandLineRecord,
) {
  return {
    costRateCents: line.costRateCents,
    billRateCents: line.billRateCents,
    hours: line.hours,
    ...(line.chapter != null ? { chapter: line.chapter } : {}),
    ...(line.metadata != null && typeof line.metadata === "object" && "location" in (line.metadata as Record<string, unknown>)
      ? { location: (line.metadata as Record<string, unknown>).location as string }
      : {}),
    ...(line.staffingAttributes != null && typeof line.staffingAttributes === "object" && "level" in (line.staffingAttributes as Record<string, unknown>)
      ? { level: (line.staffingAttributes as Record<string, unknown>).level as string }
      : {}),
  };
}

export function hasExperienceMultiplierChanges(
  line: Pick<DemandLineRecord, "costRateCents" | "billRateCents" | "hours">,
  result: ExperienceMultiplierResultRecord,
): boolean {
  return (
    result.adjustedCostRateCents !== line.costRateCents ||
    result.adjustedBillRateCents !== line.billRateCents ||
    result.adjustedHours !== line.hours
  );
}

export function buildExperienceMultiplierDemandLineUpdateData(input: {
  line: DemandLineRecord;
  result: ExperienceMultiplierResultRecord;
  multiplierSet: MultiplierSetRecord;
}): Prisma.EstimateDemandLineUncheckedUpdateInput {
  const newCostTotal = Math.round(input.result.adjustedCostRateCents * input.result.adjustedHours);
  const newPriceTotal = Math.round(input.result.adjustedBillRateCents * input.result.adjustedHours);

  return {
    costRateCents: input.result.adjustedCostRateCents,
    billRateCents: input.result.adjustedBillRateCents,
    hours: input.result.adjustedHours,
    costTotalCents: newCostTotal,
    priceTotalCents: newPriceTotal,
    metadata: {
      ...(typeof input.line.metadata === "object" && input.line.metadata !== null
        ? input.line.metadata as Record<string, unknown>
        : {}),
      experienceMultiplier: {
        setId: input.multiplierSet.id,
        setName: input.multiplierSet.name,
        appliedRules: input.result.appliedRules,
        originalCostRateCents: input.line.costRateCents,
        originalBillRateCents: input.line.billRateCents,
        originalHours: input.line.hours,
      },
    },
  };
}
