import { z } from "zod";
import {
  EstimateExportFormat,
  EstimateStatus,
  EstimateVersionStatus,
} from "../types/enums.js";

const jsonRecordSchema = z.record(z.string(), z.unknown());
const numericRecordSchema = z.record(z.string(), z.number());
const demandLineRateModeSchema = z.enum(["resource", "manual"]);

export const EstimateDemandLineCalculationMetadataSchema = z.object({
  costRateMode: demandLineRateModeSchema.default("manual"),
  billRateMode: demandLineRateModeSchema.default("manual"),
  totalMode: z.literal("computed").default("computed"),
  liveCostRateCents: z.number().int().min(0).nullable().optional(),
  liveBillRateCents: z.number().int().min(0).nullable().optional(),
  liveCurrency: z.string().length(3).nullable().optional(),
});

export const EstimateDemandLineMetadataSchema = z
  .object({
    calculation: EstimateDemandLineCalculationMetadataSchema.optional(),
  })
  .catchall(z.unknown());

export const EstimateAssumptionSchema = z.object({
  id: z.string().optional(),
  category: z.string().min(1).max(100),
  key: z.string().min(1).max(100),
  label: z.string().min(1).max(200),
  valueType: z.string().min(1).max(50).default("json"),
  value: z.unknown(),
  sortOrder: z.number().int().min(0).default(0),
  notes: z.string().max(2_000).optional(),
});

export const ScopeItemSchema = z.object({
  id: z.string().optional(),
  sequenceNo: z.number().int().min(0),
  scopeType: z.string().min(1).max(100),
  packageCode: z.string().max(100).optional(),
  name: z.string().min(1).max(500),
  description: z.string().max(5_000).optional(),
  scene: z.string().max(200).optional(),
  page: z.string().max(100).optional(),
  location: z.string().max(200).optional(),
  assumptionCategory: z.string().max(100).optional(),
  technicalSpec: jsonRecordSchema.default({}),
  frameCount: z.number().int().min(0).optional(),
  itemCount: z.number().min(0).optional(),
  unitMode: z.string().max(100).optional(),
  internalComments: z.string().max(5_000).optional(),
  externalComments: z.string().max(5_000).optional(),
  sortOrder: z.number().int().min(0).default(0),
  metadata: jsonRecordSchema.default({}),
});

export const EstimateDemandLineSchema = z.object({
  id: z.string().optional(),
  scopeItemId: z.string().optional(),
  roleId: z.string().optional(),
  resourceId: z.string().optional(),
  lineType: z.string().min(1).max(100).default("LABOR"),
  name: z.string().min(1).max(500),
  chapter: z.string().max(200).optional(),
  hours: z.number().min(0),
  days: z.number().min(0).optional(),
  fte: z.number().min(0).optional(),
  rateSource: z.string().max(200).optional(),
  costRateCents: z.number().int().min(0).default(0),
  billRateCents: z.number().int().min(0).default(0),
  currency: z.string().length(3).default("EUR"),
  costTotalCents: z.number().int().min(0).default(0),
  priceTotalCents: z.number().int().min(0).default(0),
  monthlySpread: numericRecordSchema.default({}),
  staffingAttributes: jsonRecordSchema.default({}),
  metadata: EstimateDemandLineMetadataSchema.default({}),
});

export const ResourceCostSnapshotSchema = z.object({
  id: z.string().optional(),
  resourceId: z.string().optional(),
  sourceEid: z.string().max(100).optional(),
  displayName: z.string().min(1).max(500),
  chapter: z.string().max(200).optional(),
  roleId: z.string().optional(),
  currency: z.string().length(3).default("EUR"),
  lcrCents: z.number().int().min(0),
  ucrCents: z.number().int().min(0),
  fte: z.number().min(0).optional(),
  location: z.string().max(200).optional(),
  country: z.string().max(200).optional(),
  level: z.string().max(100).optional(),
  workType: z.string().max(100).optional(),
  attributes: jsonRecordSchema.default({}),
});

export const EstimateMetricSchema = z.object({
  id: z.string().optional(),
  key: z.string().min(1).max(100),
  label: z.string().min(1).max(200),
  metricGroup: z.string().max(100).optional(),
  valueDecimal: z.number(),
  valueCents: z.number().int().optional(),
  currency: z.string().length(3).optional(),
  metadata: jsonRecordSchema.default({}),
});

export const EstimateExportSummarySchema = z.object({
  estimateId: z.string(),
  estimateName: z.string().min(1).max(500),
  versionId: z.string(),
  versionNumber: z.number().int().min(1),
  versionStatus: z.nativeEnum(EstimateVersionStatus),
  projectId: z.string().nullable().optional(),
  projectName: z.string().nullable().optional(),
  baseCurrency: z.string().length(3),
  assumptionCount: z.number().int().min(0),
  scopeItemCount: z.number().int().min(0),
  demandLineCount: z.number().int().min(0),
  resourceSnapshotCount: z.number().int().min(0),
  totalHours: z.number().min(0),
  totalCostCents: z.number().int(),
  totalPriceCents: z.number().int(),
  marginCents: z.number().int(),
  marginPercent: z.number(),
});

export const EstimateExportArtifactPayloadSchema = z.object({
  schemaVersion: z.number().int().min(1).default(1),
  format: z.nativeEnum(EstimateExportFormat),
  mimeType: z.string().min(1).max(200),
  encoding: z.enum(["utf8", "base64"]),
  fileExtension: z.string().min(1).max(20),
  generatedAt: z.string().datetime(),
  byteLength: z.number().int().min(0),
  rowCount: z.number().int().min(0).nullable().optional(),
  lineCount: z.number().int().min(0).nullable().optional(),
  sheetNames: z.array(z.string().min(1).max(200)).optional(),
  previewText: z.string().nullable().optional(),
  content: z.string(),
  summary: EstimateExportSummarySchema,
});

export const EstimateExportSchema = z.object({
  id: z.string().optional(),
  format: z.nativeEnum(EstimateExportFormat),
  fileName: z.string().min(1).max(500),
  storageKey: z.string().max(500).optional(),
  payload: z.union([EstimateExportArtifactPayloadSchema, jsonRecordSchema]).optional(),
});

export const EstimateVersionSchema = z.object({
  id: z.string().optional(),
  versionNumber: z.number().int().min(1).default(1),
  label: z.string().max(200).optional(),
  status: z.nativeEnum(EstimateVersionStatus).default(
    EstimateVersionStatus.WORKING,
  ),
  notes: z.string().max(5_000).optional(),
  lockedAt: z.coerce.date().optional(),
  projectSnapshot: jsonRecordSchema.default({}),
  assumptions: z.array(EstimateAssumptionSchema).default([]),
  scopeItems: z.array(ScopeItemSchema).default([]),
  demandLines: z.array(EstimateDemandLineSchema).default([]),
  resourceSnapshots: z.array(ResourceCostSnapshotSchema).default([]),
  metrics: z.array(EstimateMetricSchema).default([]),
  exports: z.array(EstimateExportSchema).default([]),
});

export const CreateEstimateSchema = z.object({
  projectId: z.string().optional(),
  name: z.string().min(1).max(500),
  opportunityId: z.string().max(200).optional(),
  baseCurrency: z.string().length(3).default("EUR"),
  status: z.nativeEnum(EstimateStatus).default(EstimateStatus.DRAFT),
  versionLabel: z.string().max(200).optional(),
  versionNotes: z.string().max(5_000).optional(),
  assumptions: z.array(EstimateAssumptionSchema).default([]),
  scopeItems: z.array(ScopeItemSchema).default([]),
  demandLines: z.array(EstimateDemandLineSchema).default([]),
  resourceSnapshots: z.array(ResourceCostSnapshotSchema).default([]),
  metrics: z.array(EstimateMetricSchema).default([]),
});

export const UpdateEstimateSchema = CreateEstimateSchema.partial();
export const UpdateEstimateDraftSchema = CreateEstimateSchema.partial().extend({
  id: z.string(),
  assumptions: z.array(EstimateAssumptionSchema).default([]),
  scopeItems: z.array(ScopeItemSchema).default([]),
  demandLines: z.array(EstimateDemandLineSchema).default([]),
  resourceSnapshots: z.array(ResourceCostSnapshotSchema).default([]),
  metrics: z.array(EstimateMetricSchema).default([]),
});

export const SubmitEstimateVersionSchema = z.object({
  estimateId: z.string(),
  versionId: z.string().optional(),
});

export const ApproveEstimateVersionSchema = z.object({
  estimateId: z.string(),
  versionId: z.string().optional(),
});

export const CreateEstimateRevisionSchema = z.object({
  estimateId: z.string(),
  sourceVersionId: z.string().optional(),
  label: z.string().max(200).optional(),
  notes: z.string().max(5_000).optional(),
});

export const CreateEstimateExportSchema = z.object({
  estimateId: z.string(),
  versionId: z.string().optional(),
  format: z.nativeEnum(EstimateExportFormat),
});

export const CreateEstimatePlanningHandoffSchema = z.object({
  estimateId: z.string(),
  versionId: z.string().optional(),
});

export const CloneEstimateSchema = z.object({
  sourceEstimateId: z.string(),
  name: z.string().min(1).max(500).optional(),
  projectId: z.string().optional(),
});

export const EffortUnitModeSchema = z.enum(["per_frame", "per_item", "flat"]);

export const EffortRuleSchema = z.object({
  id: z.string().optional(),
  scopeType: z.string().min(1).max(100),
  discipline: z.string().min(1).max(200),
  chapter: z.string().max(200).optional(),
  unitMode: EffortUnitModeSchema,
  hoursPerUnit: z.number().min(0),
  description: z.string().max(1000).optional(),
  sortOrder: z.number().int().min(0).default(0),
});

export const CreateEffortRuleSetSchema = z.object({
  name: z.string().min(1).max(200),
  description: z.string().max(2000).optional(),
  isDefault: z.boolean().default(false),
  rules: z.array(EffortRuleSchema).default([]),
});

export const UpdateEffortRuleSetSchema = z.object({
  id: z.string(),
  name: z.string().min(1).max(200).optional(),
  description: z.string().max(2000).optional(),
  isDefault: z.boolean().optional(),
  rules: z.array(EffortRuleSchema).optional(),
});

export const ApplyEffortRulesSchema = z.object({
  estimateId: z.string(),
  ruleSetId: z.string(),
  mode: z.enum(["replace", "append"]).default("replace"),
});

export type CreateEffortRuleSetInput = z.infer<typeof CreateEffortRuleSetSchema>;
export type UpdateEffortRuleSetInput = z.infer<typeof UpdateEffortRuleSetSchema>;
export type ApplyEffortRulesInput = z.infer<typeof ApplyEffortRulesSchema>;

// ─── Experience Multipliers ──────────────────────────────────────────────────

export const ExperienceMultiplierRuleSchema = z.object({
  id: z.string().optional(),
  chapter: z.string().max(200).optional(),
  location: z.string().max(200).optional(),
  level: z.string().max(100).optional(),
  costMultiplier: z.number().min(0).default(1.0),
  billMultiplier: z.number().min(0).default(1.0),
  shoringRatio: z.number().min(0).max(1).optional(),
  additionalEffortRatio: z.number().min(0).optional(),
  description: z.string().max(1000).optional(),
  sortOrder: z.number().int().min(0).default(0),
});

export const CreateExperienceMultiplierSetSchema = z.object({
  name: z.string().min(1).max(200),
  description: z.string().max(2000).optional(),
  isDefault: z.boolean().default(false),
  rules: z.array(ExperienceMultiplierRuleSchema).default([]),
});

export const UpdateExperienceMultiplierSetSchema = z.object({
  id: z.string(),
  name: z.string().min(1).max(200).optional(),
  description: z.string().max(2000).optional(),
  isDefault: z.boolean().optional(),
  rules: z.array(ExperienceMultiplierRuleSchema).optional(),
});

export const ApplyExperienceMultipliersSchema = z.object({
  estimateId: z.string(),
  multiplierSetId: z.string(),
});

export type CreateExperienceMultiplierSetInput = z.infer<typeof CreateExperienceMultiplierSetSchema>;
export type UpdateExperienceMultiplierSetInput = z.infer<typeof UpdateExperienceMultiplierSetSchema>;
export type ApplyExperienceMultipliersInput = z.infer<typeof ApplyExperienceMultipliersSchema>;

export const EstimateListFiltersSchema = z.object({
  projectId: z.string().optional(),
  status: z.nativeEnum(EstimateStatus).optional(),
  query: z.string().max(200).optional(),
});

export const PhasingPatternSchema = z.enum([
  "even",
  "front_loaded",
  "back_loaded",
  "custom",
]);

export const GenerateWeeklyPhasingSchema = z.object({
  estimateId: z.string(),
  startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  pattern: PhasingPatternSchema.default("even"),
});

export const UpdateWeeklyPhasingSchema = z.object({
  estimateId: z.string(),
  demandLineId: z.string(),
  weeklyHours: z.record(z.string(), z.number().min(0)),
});

export type GenerateWeeklyPhasingInput = z.infer<typeof GenerateWeeklyPhasingSchema>;
export type UpdateWeeklyPhasingInput = z.infer<typeof UpdateWeeklyPhasingSchema>;

// ─── Commercial Terms ───────────────────────────────────────────────────────

export const PricingModelSchema = z.enum(["fixed_price", "time_and_materials", "hybrid"]);

export const PaymentMilestoneSchema = z.object({
  label: z.string().min(1).max(200),
  percent: z.number().min(0).max(100),
  dueDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
  description: z.string().max(1000).nullable().optional(),
});

export const CommercialTermsSchema = z.object({
  pricingModel: PricingModelSchema.default("fixed_price"),
  contingencyPercent: z.number().min(0).max(100).default(0),
  discountPercent: z.number().min(0).max(100).default(0),
  paymentTermDays: z.number().int().min(0).max(365).default(30),
  paymentMilestones: z.array(PaymentMilestoneSchema).default([]),
  warrantyMonths: z.number().int().min(0).max(60).default(0),
  notes: z.string().max(5_000).nullable().optional(),
});

export const UpdateCommercialTermsSchema = z.object({
  estimateId: z.string(),
  versionId: z.string().optional(),
  terms: CommercialTermsSchema,
});

export type CommercialTermsInput = z.infer<typeof CommercialTermsSchema>;
export type UpdateCommercialTermsInput = z.infer<typeof UpdateCommercialTermsSchema>;

export type CreateEstimateInput = z.infer<typeof CreateEstimateSchema>;
export type UpdateEstimateInput = z.infer<typeof UpdateEstimateSchema>;
export type UpdateEstimateDraftInput = z.infer<typeof UpdateEstimateDraftSchema>;
export type EstimateListFilters = z.infer<typeof EstimateListFiltersSchema>;
export type SubmitEstimateVersionInput = z.infer<
  typeof SubmitEstimateVersionSchema
>;
export type ApproveEstimateVersionInput = z.infer<
  typeof ApproveEstimateVersionSchema
>;
export type CreateEstimateRevisionInput = z.infer<
  typeof CreateEstimateRevisionSchema
>;
export type CreateEstimateExportInput = z.infer<
  typeof CreateEstimateExportSchema
>;
export type CreateEstimatePlanningHandoffInput = z.infer<
  typeof CreateEstimatePlanningHandoffSchema
>;
export type CloneEstimateInput = z.infer<typeof CloneEstimateSchema>;
