import { DeepPartial, Path } from "react-hook-form";
import * as z from "zod";
import { HAZARD_CLASS_VALUES } from "@edr/types";

// Wizard steps for the contract creation flow. Condensed to four steps: the
// pickers are dropdown selects so each step fits one screen without scrolling.
// Cargo collects SCOPE only (no quantities); the route step also collects a
// non-binding estimated shipment date (the binding date is at booking time).
export const CONTRACT_STEPS = [
  { id: 0, label: "Setup", short: "Setup" },
  { id: 1, label: "Cargo & Route", short: "Cargo & Route" },
  { id: 2, label: "Review & Submit", short: "Review" },
] as const;

export const OPERATION_TYPES = [
  "import",
  "export",
  "intercity",
  "import_ff",
  "export_ff",
] as const;
export type OperationType = (typeof OPERATION_TYPES)[number];

export const OPERATION_TYPE_OPTIONS: Array<{
  value: OperationType;
  label: string;
  description: string;
  /** Shown but not selectable (service not offered yet). */
  disabled?: boolean;
}> = [
  {
    value: "import",
    label: "Import",
    description: "Cargo arriving into Ethiopia via Djibouti.",
  },
  {
    value: "export",
    label: "Export",
    description: "Cargo leaving Ethiopia bound for Djibouti.",
  },
  {
    value: "intercity",
    label: "Intercity",
    description:
      "Domestic movement between Ethiopian yards — rides on passing import/export trains, no customs.",
  },
  {
    value: "import_ff",
    label: "Import as Freight Forwarder",
    description: "Import handled on behalf of a client.",
  },
  {
    value: "export_ff",
    label: "Export as Freight Forwarder",
    description: "Export handled on behalf of a client.",
  },
];

export const CONTRACT_KIND_OPTIONS: Array<{
  value: ContractKindOption;
  label: string;
  description: string;
}> = [
  {
    value: "one_time",
    label: "One-Time Contract",
    description: "A single shipment cycle — one active booking at a time.",
  },
  {
    value: "general_contract",
    label: "General Contract",
    description: "Ship multiple times over the validity window on one route.",
  },
];

export type ContractDocuments = Record<string, File | File[] | null>;

export const PAYMENT_CURRENCIES = ["USD", "ETB"] as const;
export type PaymentCurrency = (typeof PAYMENT_CURRENCIES)[number];

export const PAYMENT_CURRENCY_OPTIONS: Array<{
  value: PaymentCurrency;
  label: string;
  description: string;
}> = [
  {
    value: "USD",
    label: "USD",
    description: "US Dollar — international pricing and invoicing.",
  },
  {
    value: "ETB",
    label: "ETB",
    description: "Ethiopian Birr — local pricing and invoicing.",
  },
];

// one_time → ContractKind.OneTime; general_contract → ContractKind.General.
export const CONTRACT_KINDS = ["one_time", "general_contract"] as const;
export type ContractKindOption = (typeof CONTRACT_KINDS)[number];

export const CONTAINER_SIZES = ["20ft", "40ft"] as const;
export type ContainerSize = (typeof CONTAINER_SIZES)[number];

// A GENERAL-contract quantity cap. The Mantine NumberInput backing these fields
// can briefly emit "" / undefined / NaN (cleared or never-touched field);
// coerce those to 0 so the superRefine below can flag them with a clear
// "greater than 0" message instead of a type error.
const nonNegativeQuantityCap = z.preprocess(
  (v) =>
    v === "" || v === null || v === undefined || Number.isNaN(v) ? 0 : v,
  z.number().nonnegative(),
);

export const contractFormSchema = z
  .object({
    // Operation drives trade direction (import/export → IMPORT/EXPORT;
    // intercity → DOMESTIC), gated by the company's onboarded profiles.
    operationType: z.enum(OPERATION_TYPES, "Select an operation type."),
    // One-time vs. general contract.
    contractKind: z.enum(CONTRACT_KINDS).default("one_time"),
    contractType: z.enum(["new", "renewal"], "Select a contract type."),
    previousContractRef: z.string().default(""),

    serviceTypeId: z
      .string("Select a service type.")
      .min(1, "Select a service type."),

    firstMile: z
      .object({
        enabled: z.boolean().default(false),
        pickUpAddress: z.string().default(""),
        // Optional free-text exact location (gate, building, landmark…).
        exactLocation: z.string().default(""),
        lat: z.number().nullable().default(null),
        lng: z.number().nullable().default(null),
      })
      .refine((data) => !(data.enabled && !data.pickUpAddress.trim()), {
        message: "Select the pick-up location on the map.",
        path: ["pickUpAddress"],
      }),
    lastMile: z
      .object({
        enabled: z.boolean().default(false),
        deliveryAddress: z.string().default(""),
        // Optional free-text exact location (gate, building, landmark…).
        exactLocation: z.string().default(""),
        lat: z.number().nullable().default(null),
        lng: z.number().nullable().default(null),
      })
      .refine((data) => !(data.enabled && !data.deliveryAddress.trim()), {
        message: "Select the delivery location on the map.",
        path: ["deliveryAddress"],
      }),
    equipmentReturn: z
      .enum(["with_return", "without_return"])
      .default("without_return"),
    customsClearingEnabled: z.boolean().default(false),
    customsClearingAgent: z.string().default(""),

    // ── Cargo SCOPE (no quantities) ──
    cargoType: z.enum(["container", "bulk"], "Select a cargo type."),
    // Container scope: ALWAYS both sizes — the contract covers 20ft and 40ft
    // (both rates shown); the customer picks quantities per booking, where a
    // size can be 0. No picker in the UI; kept for review display/prefill.
    enabledContainerSizes: z.array(z.enum(CONTAINER_SIZES)).default([]),
    // GENERAL only: per-size container quantity cap (total bookable over the
    // validity window). Keyed by size; must be > 0 for every enabled size
    // (enforced in the superRefine below).
    containerSizeCaps: z
      .record(z.string(), nonNegativeQuantityCap)
      .default({}),
    // Bulk scope: the cargo type path (group → commodity).
    cargoTypePath: z.array(z.string()).default([]),
    cargoFreeText: z.string().default(""),
    // GENERAL only: total bulk tons/items bookable; must be > 0 (superRefine).
    bulkQuantityCap: nonNegativeQuantityCap.default(0),
    // Contract-level billing flags.
    isHazardous: z.boolean().default(false),
    // Dangerous-goods declaration — collected with the hazard documents and
    // mandatory whenever isHazardous (enforced in the superRefine below).
    hazardClass: z.string().default(""),
    unNumber: z.string().default(""),
    isRefrigerated: z.boolean().default(false),

    // ── Route ── (one route per contract — general contracts included)
    originYard: z.string().min(1, "Select an origin yard."),
    destinationYard: z.string().min(1, "Select a destination yard."),

    documents: z.record(z.string(), z.any()).default({}),
    notes: z.string().default(""),
  })
  .refine(
    (data) =>
      !(data.contractType === "renewal" && !data.previousContractRef.trim()),
    {
      message: "Enter a previous contract reference.",
      path: ["previousContractRef"],
    },
  )
  .refine((data) => data.originYard !== "", {
    message: "Select an origin yard.",
    path: ["originYard"],
  })
  .refine((data) => data.destinationYard !== "", {
    message: "Select a destination yard.",
    path: ["destinationYard"],
  })
  .refine(
    (data) =>
      !(
        data.originYard &&
        data.destinationYard &&
        data.originYard === data.destinationYard
      ),
    {
      message: "Destination must be different from origin.",
      path: ["destinationYard"],
    },
  )
  .superRefine((data, ctx) => {
    // Container scope needs no validation: both sizes are always in scope and
    // the cargo description moved to booking time.
    if (data.cargoType === "bulk") {
      // Bulk scope: a commodity is required.
      if (!data.cargoTypePath[0]) {
        ctx.addIssue({
          code: "custom",
          path: ["cargoTypePath"],
          message: "Select a cargo type.",
        });
      } else if (!data.cargoTypePath[1]) {
        ctx.addIssue({
          code: "custom",
          path: ["cargoTypePath"],
          message: "Select a commodity.",
        });
      }
    }
    // Hazardous cargo must name its UN/ADR class and UN number — the API
    // rejects the contract otherwise, so catch it before the wizard submits.
    if (data.isHazardous) {
      if (!HAZARD_CLASS_VALUES.includes(data.hazardClass)) {
        ctx.addIssue({
          code: "custom",
          path: ["hazardClass"],
          message: "Select the dangerous-goods class.",
        });
      }
      if (!data.unNumber.trim()) {
        ctx.addIssue({
          code: "custom",
          path: ["unNumber"],
          message: "Enter the UN number.",
        });
      }
    }
    // GENERAL contracts are uncapped: no quantity cap is collected, so the
    // customer can book repeatedly until the contract's validity expires. The
    // cap fields default to 0/empty and map to quantityCap = NULL (uncapped) at
    // the API. No cap validation is applied.
  });

export type ContractFormValues = z.infer<typeof contractFormSchema>;
export type ContractFormInputValues = z.input<typeof contractFormSchema>;

export const initialContractFormValues: DeepPartial<ContractFormValues> = {
  contractKind: "one_time",
  contractType: "new",
  previousContractRef: "",

  serviceTypeId: "",
  firstMile: { enabled: false, pickUpAddress: "", exactLocation: "", lat: null, lng: null },
  lastMile: { enabled: false, deliveryAddress: "", exactLocation: "", lat: null, lng: null },
  equipmentReturn: "without_return",
  customsClearingEnabled: false,
  customsClearingAgent: "",

  cargoType: "container",
  enabledContainerSizes: [...CONTAINER_SIZES],
  containerSizeCaps: {},
  cargoTypePath: [],
  cargoFreeText: "",
  bulkQuantityCap: 0,
  isHazardous: false,
  hazardClass: "",
  unNumber: "",
  isRefrigerated: false,

  originYard: "",
  destinationYard: "",

  documents: {},
  notes: "",
};

export const contractStepFields: Record<
  number,
  Array<Path<ContractFormValues>>
> = {
  // Step 0 — Setup: operation, contract kind/type, service, currency, miles.
  0: [
    "operationType",
    "contractKind",
    "contractType",
    "previousContractRef",
    "serviceTypeId",
    "equipmentReturn",
    "customsClearingEnabled",
    "customsClearingAgent",
    "firstMile",
    "lastMile",
  ],
  // Step 1 — Cargo & Route: scope, sizes, flags, origin/destination.
  1: [
    "cargoType",
    "enabledContainerSizes",
    "containerSizeCaps",
    "cargoTypePath",
    "cargoFreeText",
    "bulkQuantityCap",
    "isHazardous",
    "hazardClass",
    "unNumber",
    "isRefrigerated",
    "originYard",
    "destinationYard",
  ],
  // Step 2 — Review & Submit. (The separate Documents step was removed — the
  // company profile documents are attached to the contract automatically.)
  2: ["notes"],
};
