import type { Prisma } from "@capakraken/db";
import type {
  EffortRuleInput,
  ScopeItemInput,
} from "@capakraken/engine";
import {
  CreateEffortRuleSetSchema,
  UpdateEffortRuleSetSchema,
} from "@capakraken/shared";
import { z } from "zod";

type CreateEffortRuleSetInput = z.infer<typeof CreateEffortRuleSetSchema>;
type UpdateEffortRuleSetInput = z.infer<typeof UpdateEffortRuleSetSchema>;
type EffortRuleRowInput =
  | CreateEffortRuleSetInput["rules"][number]
  | NonNullable<UpdateEffortRuleSetInput["rules"]>[number];

type EffortRuleRecord = {
  scopeType: string;
  discipline: string;
  chapter: string | null;
  unitMode: string;
  hoursPerUnit: number;
  sortOrder: number;
};

type ScopeItemRecord = {
  name: string;
  scopeType: string;
  frameCount: number | null;
  itemCount: number | null;
  unitMode: string | null;
};

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

type ExpandedEffortLineRecord = {
  scopeItemName: string;
  discipline: string;
  chapter?: string | null;
  hours: number;
  unitMode: string;
  unitCount: number;
  hoursPerUnit: number;
};

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

function buildEffortRuleRow(input: EffortRuleRowInput, index: number) {
  return {
    scopeType: input.scopeType,
    discipline: input.discipline,
    ...(input.chapter ? { chapter: input.chapter } : {}),
    unitMode: input.unitMode,
    hoursPerUnit: input.hoursPerUnit,
    ...(input.description ? { description: input.description } : {}),
    sortOrder: input.sortOrder ?? index,
  };
}

export function buildEffortRuleNestedCreateRows(
  rules: EffortRuleRowInput[],
): Prisma.EffortRuleUncheckedCreateWithoutRuleSetInput[] {
  return rules.map((rule, index) => ({
    ...buildEffortRuleRow(rule, index),
  }));
}

export function buildEffortRuleCreateManyRows(
  rules: EffortRuleRowInput[],
  ruleSetId: string,
): Prisma.EffortRuleCreateManyInput[] {
  return rules.map((rule, index) => ({
    ruleSetId,
    ...buildEffortRuleRow(rule, index),
  }));
}

export function buildEffortRuleSetCreateData(
  input: CreateEffortRuleSetInput,
): Prisma.EffortRuleSetCreateInput {
  return {
    name: input.name,
    ...(input.description ? { description: input.description } : {}),
    isDefault: input.isDefault,
    rules: {
      create: buildEffortRuleNestedCreateRows(input.rules),
    },
  };
}

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

export function toScopeItemInputs(
  scopeItems: ScopeItemRecord[],
): ScopeItemInput[] {
  return scopeItems.map((scopeItem) => ({
    name: scopeItem.name,
    scopeType: scopeItem.scopeType as ScopeItemInput["scopeType"],
    frameCount: scopeItem.frameCount,
    itemCount: scopeItem.itemCount,
    unitMode: (scopeItem.unitMode ?? null) as Exclude<ScopeItemInput["unitMode"], undefined>,
  }) as ScopeItemInput);
}

export function toEffortRuleEngineInputs(
  rules: EffortRuleRecord[],
): EffortRuleInput[] {
  return rules.map((rule) => ({
    scopeType: rule.scopeType as EffortRuleInput["scopeType"],
    discipline: rule.discipline,
    chapter: rule.chapter,
    unitMode: rule.unitMode as EffortRuleInput["unitMode"],
    hoursPerUnit: rule.hoursPerUnit,
    sortOrder: rule.sortOrder,
  }));
}

export function buildEstimateDemandLineRows(input: {
  estimateVersionId: string;
  currency: string;
  ruleSet: DemandLineRuleSetRecord;
  lines: ExpandedEffortLineRecord[];
}): Prisma.EstimateDemandLineUncheckedCreateInput[] {
  return input.lines.map((line) => ({
    estimateVersionId: input.estimateVersionId,
    lineType: "LABOR",
    name: `${line.discipline} — ${line.scopeItemName}`,
    ...(line.chapter ? { chapter: line.chapter } : {}),
    hours: line.hours,
    costRateCents: 0,
    billRateCents: 0,
    currency: input.currency,
    costTotalCents: 0,
    priceTotalCents: 0,
    monthlySpread: {},
    staffingAttributes: {},
    metadata: {
      effortRule: {
        ruleSetId: input.ruleSet.id,
        ruleSetName: input.ruleSet.name,
        discipline: line.discipline,
        unitMode: line.unitMode,
        unitCount: line.unitCount,
        hoursPerUnit: line.hoursPerUnit,
      },
    },
  }));
}
