import { DeepPartial, Path } from "react-hook-form";
import * as z from "zod";

export const ETHIOPIA_STATIONS = new Set<string>([
  "Addis Ababa",
  "Adama",
  "Mojo",
  "Awash",
  "Mieso",
  "Dire Dawa",
]);

export const MOCK_VALID_CONTRACTS = [
  "EDR-2024-10001",
  "EDR-2024-10002",
  "EDR-2023-88123",
  "EDR-2022-55442",
];

export const STEPS = [
  { id: 1, label: "Contract Type", short: "Contract" },
  { id: 2, label: "Service Type & Mile", short: "Service" },
  { id: 3, label: "Route", short: "Route" },
  { id: 4, label: "Cargo Details", short: "Cargo" },
  { id: 5, label: "Review & Submit", short: "Submit" },
] as const;

export const bookingFormSchema = z
  .object({
    contractType: z.enum(["new", "renewal"], "Select a contract type."),
    previousContractRef: z.string(),
    serviceType: z.enum(["rail", "rail_forwarding"], "Select a service type."),
    firstMile: z
      .object({
        enabled: z.boolean().default(false),
        pickUpAddress: z.string(),
      })
      .refine((data) => !(data.enabled && !data.pickUpAddress.trim()), {
        message: "Enter the pick-up address.",
        path: ["pickUpAddress"],
      }),
    lastMile: z
      .object({
        enabled: z.boolean().default(false),
        deliveryAddress: z.string(),
      })
      .refine((data) => !(data.enabled && !data.deliveryAddress.trim()), {
        message: "Enter the delivery address.",
        path: ["deliveryAddress"],
      }),
    equipmentReturn: z
      .enum(["with_return", "without_return"])
      .default("with_return"),
    customsClearingEnabled: z.boolean().default(false),
    originYard: z.string().min(1, "Select an origin yard."),
    destinationYard: z.string().min(1, "Select a destination yard."),
    shippingLine: z.string(),
    cargoType: z.enum(["container", "bulk"], "Select a cargo type."),
    cargoWeight: z.string(),
    freightType: z.string(), // parent group
    bulkCommoditytype: z.string(),
    isHazardous: z.boolean(),
    isRefrigerated: z.boolean(),
    containers: z.array(
      z.object({
        type: z.enum(["20ft", "40ft"]),
        containerType: z.string().min(1, "Select a container type."),
        qty: z
          .string()
          .refine((q) => q.length !== 0, "Quantity is required.")
          .refine((q) => !isNaN(+q), "Enter a valid Number")
          .refine((qty) => Number(qty) >= 1, "Must be greater than 0"),
        vgm: z
          .string()
          .refine((vgm) => vgm.length !== 0, "VGM is required.")
          .refine((vgm) => !isNaN(+vgm), "Enter a valid Number")
          .refine((vgm) => Number(vgm) >= 0, "Must be greater than 0"),
      }),
    ),
    consolidationEnabled: z.boolean(),
    notes: z.string(),
    termsAccepted: z.boolean(),
  })
  .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"],
    },
  )
  .refine((data) => !(data.cargoType === "bulk" && !data.freightType), {
    message: "Select a freight type.",
    path: ["freightType"],
  })
  .refine(
    (data) =>
      !(
        data.cargoType === "bulk" &&
        data.freightType &&
        !data.bulkCommoditytype
      ),
    { message: "Select a commodity.", path: ["bulkCommoditytype"] },
  )
  .refine(
    (data) => {
      if (data.cargoType !== "bulk") return true;
      const cargoWeight = Number(data.cargoWeight);
      return (
        !!data.cargoWeight && !Number.isNaN(cargoWeight) && cargoWeight > 0
      );
    },
    { message: "Enter a cargo weight greater than 0.", path: ["cargoWeight"] },
  )
  .refine(
    (data) => !(data.cargoType === "container" && data.containers.length === 0),
    { message: "Add at least one container.", path: ["containers"] },
  )
  .refine((data) => data.termsAccepted, {
    message: "Accept the freight contract terms to submit.",
    path: ["termsAccepted"],
  })
  .superRefine((data, ctx) => {
    if (data.cargoType === "container") {
      data.containers.forEach((c, i) => {
        if (!c.qty || +c.qty < 1) {
          ctx.addIssue({
            code: "custom",
            path: ["containers", i, "qty"],
            message: "Enter at least 1 container.",
          });
        }

        if (!c.vgm || +c.vgm <= 0) {
          ctx.addIssue({
            code: "custom",
            path: ["containers", i, "vgm"],
            message: "Enter VGM greater than 0.",
          });
        }
      });
    }
  });

export type BookingFormValues = z.infer<typeof bookingFormSchema>;
export type BookingFormInputValues = z.input<typeof bookingFormSchema>;

export const initialBookingFormValues: DeepPartial<BookingFormValues> = {
  previousContractRef: "",

  firstMile: {
    enabled: false,
    pickUpAddress: "",
  },
  lastMile: {
    enabled: false,
    deliveryAddress: "",
  },
  equipmentReturn: "with_return",
  customsClearingEnabled: false,
  originYard: "",
  destinationYard: "",
  shippingLine: "",
  cargoWeight: "",
  bulkCommoditytype: "",
  isHazardous: false,
  isRefrigerated: false,
  containers: [{ type: "20ft", containerType: "", qty: "1", vgm: "" }],
  consolidationEnabled: false,
  notes: "",
  termsAccepted: false,
};

export const stepFields: Record<number, Array<Path<BookingFormValues>>> = {
  1: ["contractType", "previousContractRef"],
  2: [
    "serviceType",
    "firstMile",
    "lastMile",
    "equipmentReturn",
    "customsClearingEnabled",
  ],
  3: [
    "originYard",
    "destinationYard",
    "isHazardous",
    "isRefrigerated",
    "shippingLine",
  ],
  4: [
    "cargoType",
    "cargoWeight",
    "freightType",
    "bulkCommoditytype",
    "containers",
    "consolidationEnabled",
  ],
  5: ["notes", "termsAccepted"],
};

export type RouteDirection = "import" | "export" | "domestic" | null;

export interface ContainerConfig {
  type: "20ft" | "40ft";
  containerType: string;
  qty: string;
  vgm: string;
}

export interface WagonConfig {
  type: "20ft" | "40ft";
}

export interface WagonCalcResult {
  totalWagons: number;
  hasOddUnit: boolean;
  sharedWagons: number;
  wagonLayout: WagonConfig[];
  ft40Wagons: number;
  ft20Wagons: number;
}

export function genContractId(): string {
  const yr = new Date().getFullYear();
  const n = Math.floor(10000 + Math.random() * 90000);
  return `EDR-DRAFT-${yr}-${n}`;
}

export function getRouteDirection(
  origin: string,
  dest: string,
): RouteDirection {
  if (!origin || !dest) return null;
  const oLocation = getStationLocation(origin);
  const dLocation = getStationLocation(dest);
  if (oLocation === "inside" && dLocation === "outside") return "export";
  if (oLocation === "outside" && dLocation === "inside") return "import";
  if (oLocation === "inside" && dLocation === "inside") return "domestic";

  const oEth = ETHIOPIA_STATIONS.has(origin);
  const dEth = ETHIOPIA_STATIONS.has(dest);
  if (oEth && !dEth) return "export";
  if (!oEth && dEth) return "import";
  if (oEth && dEth) return "domestic";
  return null;
}

function getStationLocation(value: string): "inside" | "outside" | null {
  const normalized = value.trim().toLowerCase();
  if (normalized.startsWith("inside")) return "inside";
  if (normalized.startsWith("outside")) return "outside";
  return null;
}

export function calcWagons(containers: ContainerConfig[]): WagonCalcResult {
  const Ft40Wagons = containers
    .filter((c) => c.type === "40ft")
    .reduce((sum, c) => sum + Number(c.qty), 0);
  const Ft20Wagons = containers
    .filter((c) => c.type === "20ft")
    .reduce((sum, c) => sum + Number(c.qty), 0);
  const wagonLayout: WagonConfig[] = [];
  let hasOddUnit = Ft20Wagons % 2 === 1;
  let sharedWagons = Math.floor(Ft20Wagons / 2);

  return {
    totalWagons: sharedWagons + Ft40Wagons,
    hasOddUnit,
    sharedWagons,
    ft40Wagons: Ft40Wagons,
    ft20Wagons: Ft20Wagons,
    wagonLayout,
  };
}
