import type { LucideIcon } from "lucide-react";
import {
  Ban,
  Check,
  Coins,
  FileSignature,
  MessageSquareWarning,
  Play,
  ShieldCheck,
  TrainTrack,
  Truck,
  XCircle,
} from "lucide-react";

import type { AuthUser } from "@/auth/types";
import {
  FREIGHT_PERMS,
  hasPermission,
  isFreightApprovalAdmin,
} from "@/lib/permissions";
import type {
  BookingApprovalStep,
  BookingDetail,
  BookingStatus,
} from "@/types/booking";

export type BookingActionId =
  | "accept"
  | "requestChanges"
  | "reject"
  | "approve"
  | "rejectApproval"
  | "viewContract"
  | "signContractStaff"
  | "allocateBooking"
  | "startTransit"
  | "complete"
  | "operationAccept"
  | "operationRequestChanges"
  | "operationAdjustPrice"
  | "cancel";

export type BookingActionInputKind =
  | "note"
  | "reason"
  | "file"
  | "days"
  | "amount";

export interface BookingActionDef {
  id: BookingActionId;
  label: string;
  shortLabel: string;
  description: string;
  confirmTitle: string;
  confirmDescription: string;
  variant: "default" | "destructive" | "outline";
  icon: LucideIcon;
  input?: BookingActionInputKind;
  inputLabel?: string;
  inputPlaceholder?: string;
  primary?: boolean;
}

export type BookingActionContext = Pick<
  BookingDetail,
  | "status"
  | "paymentCurrency"
  | "approvalSteps"
  | "reference"
  | "schedulingStatus"
>;

const ALLOCATABLE_SCHEDULING_STATUSES = new Set([
  "NOT_SCHEDULED",
  "HOLDING",
  "ELIGIBLE",
  undefined,
  null,
  "",
]);

export function canAllocateBooking(
  booking: Pick<BookingDetail, "status" | "schedulingStatus">,
) {
  return (
    booking.status === "PAID" &&
    ALLOCATABLE_SCHEDULING_STATUSES.has(booking.schedulingStatus ?? undefined)
  );
}

export function getNextPendingApprovalStep(
  steps?: BookingApprovalStep[] | null,
): BookingApprovalStep | undefined {
  if (!steps?.length) return undefined;
  return [...steps]
    .sort((a, b) => a.stepOrder - b.stepOrder)
    .find((s) => s.status === "PENDING");
}

function approvalActions(
  steps?: BookingApprovalStep[] | null,
): BookingActionDef[] {
  const next = getNextPendingApprovalStep(steps);
  if (!next) return [];
  return [
    buildApproveActionForStep(next),
    {
      id: "rejectApproval",
      label: "Reject approval",
      shortLabel: "Reject",
      description: "Reject at the current approval step",
      confirmTitle: "Reject at approval step?",
      confirmDescription:
        "The booking will be marked rejected. This action cannot be undone from the UI.",
      variant: "destructive",
      icon: XCircle,
      input: "reason",
      inputLabel: "Rejection reason",
      inputPlaceholder: "Explain why this booking is rejected…",
    },
  ];
}

const SUBMITTED_ACTIONS: BookingActionDef[] = [
  {
    id: "accept",
    label: "Accept for approval",
    shortLabel: "Accept",
    description: "Start the formal approval chain",
    confirmTitle: "Accept submission?",
    confirmDescription:
      "Set how long the contract stays valid, then the booking moves to pending approval and approval steps are created from the rule engine.",
    variant: "default",
    icon: ShieldCheck,
    primary: true,
    input: "days",
    inputLabel: "Contract validity (days)",
    inputPlaceholder: "e.g. 30",
  },
  {
    id: "requestChanges",
    label: "Request changes",
    shortLabel: "Changes",
    description: "Ask the customer to update and resubmit",
    confirmTitle: "Request changes from customer?",
    confirmDescription:
      "The customer will see your note and can edit the booking before resubmitting.",
    variant: "outline",
    icon: MessageSquareWarning,
    input: "note",
    inputLabel: "Message to customer",
    inputPlaceholder: "Describe what needs to be corrected or added…",
  },
  {
    id: "reject",
    label: "Reject booking",
    shortLabel: "Reject",
    description: "Reject this submission",
    confirmTitle: "Reject booking?",
    confirmDescription:
      "The booking will be marked rejected and removed from active queues.",
    variant: "destructive",
    icon: Ban,
    input: "reason",
    inputLabel: "Rejection reason",
    inputPlaceholder: "Reason for rejection…",
  },
];

// Marketing/operations review of a drawdown order's operation request.
const OPERATION_REVIEW_ACTIONS: BookingActionDef[] = [
  {
    id: "operationAccept",
    label: "Accept operation",
    shortLabel: "Accept",
    description: "Accept the operation request and release it for dispatch",
    confirmTitle: "Accept operation request?",
    confirmDescription:
      "Train orders enter the batch pool; road orders move to truck dispatch.",
    variant: "default",
    icon: Check,
    primary: true,
  },
  {
    id: "operationRequestChanges",
    label: "Request changes",
    shortLabel: "Changes",
    description: "Ask the customer to adjust the operation request",
    confirmTitle: "Request changes to the operation?",
    confirmDescription:
      "The customer will see your note and can adjust and resubmit the order.",
    variant: "outline",
    icon: MessageSquareWarning,
    input: "note",
    inputLabel: "Message to customer",
    inputPlaceholder: "Describe what needs to change…",
  },
  {
    id: "operationAdjustPrice",
    label: "Adjust price",
    shortLabel: "Price",
    description: "Set an adjusted total the customer must confirm",
    confirmTitle: "Adjust the order price?",
    confirmDescription:
      "Enter the new total. The customer must confirm it before the order proceeds.",
    variant: "outline",
    icon: Coins,
    input: "amount",
    inputLabel: "Adjusted total",
    inputPlaceholder: "0.00",
  },
];

const CANCEL_ACTION: BookingActionDef = {
  id: "cancel",
  label: "Cancel booking",
  shortLabel: "Cancel",
  description: "Cancel this booking",
  confirmTitle: "Cancel booking?",
  confirmDescription:
    "The booking will be marked cancelled. Provide a reason for the audit trail.",
  variant: "destructive",
  icon: Ban,
  input: "reason",
  inputLabel: "Cancellation reason",
  inputPlaceholder: "Reason for cancellation…",
};

const VIEW_CONTRACT_ACTION: BookingActionDef = {
  id: "viewContract",
  label: "View contract",
  shortLabel: "Contract",
  description: "Open contract document and signatures",
  confirmTitle: "",
  confirmDescription: "",
  variant: "outline",
  icon: FileSignature,
};

const SIGN_CONTRACT_STAFF_ACTION: BookingActionDef = {
  id: "signContractStaff",
  label: "Sign contract",
  shortLabel: "Sign",
  description: "Open contract page and apply staff counter-signature",
  confirmTitle: "",
  confirmDescription: "",
  variant: "default",
  icon: FileSignature,
  primary: true,
};

function withCancel(actions: BookingActionDef[]): BookingActionDef[] {
  return [...actions, CANCEL_ACTION];
}

const ACTION_PERMISSION: Partial<Record<BookingActionId, string>> = {
  accept: FREIGHT_PERMS.bookings.staffAccept,
  requestChanges: FREIGHT_PERMS.bookings.requestChanges,
  reject: FREIGHT_PERMS.bookings.reject,
  rejectApproval: FREIGHT_PERMS.bookings.rejectApproval,
  viewContract: FREIGHT_PERMS.bookings.view,
  signContractStaff: FREIGHT_PERMS.bookings.signStaff,
  startTransit: FREIGHT_PERMS.bookings.operations,
  complete: FREIGHT_PERMS.bookings.operations,
  operationAccept: FREIGHT_PERMS.bookings.operations,
  operationRequestChanges: FREIGHT_PERMS.bookings.operations,
  operationAdjustPrice: FREIGHT_PERMS.bookings.operations,
  allocateBooking: FREIGHT_PERMS.trainScheduling.manage,
  cancel: FREIGHT_PERMS.bookings.cancel,
};

const approvePermissionForRole = (role: string): string | undefined => {
  if (role === "LINE_STAFF") return FREIGHT_PERMS.bookings.approveLineStaff;
  if (role === "DIRECTOR") return FREIGHT_PERMS.bookings.approveDirector;
  if (role === "CEO") return FREIGHT_PERMS.bookings.approveCeo;
  return undefined;
};

/** True when this step is the current pending step and the user may approve it. */
export function canActOnApprovalStep(
  user: AuthUser | null | undefined,
  step: BookingApprovalStep,
  steps?: BookingApprovalStep[] | null,
): boolean {
  if (step.status !== "PENDING") return false;
  const next = getNextPendingApprovalStep(steps);
  if (!next || next.id !== step.id) return false;
  if (isFreightApprovalAdmin(user)) return true;
  const perm = approvePermissionForRole(step.requiredRole);
  return perm ? hasPermission(user, perm) : false;
}

export function buildApproveActionForStep(
  step: BookingApprovalStep,
): BookingActionDef {
  return {
    id: "approve",
    label: `Approve (${step.requiredRole})`,
    shortLabel: "Approve",
    description: `Complete step ${step.stepOrder} as ${step.requiredRole}`,
    confirmTitle: `Approve as ${step.requiredRole}?`,
    confirmDescription:
      "This records your approval and advances the booking to the next step in the chain.",
    variant: "default",
    icon: Check,
    primary: true,
  };
}

function filterActionsByUser(
  actions: BookingActionDef[],
  user: AuthUser | null | undefined,
  approvalSteps?: BookingApprovalStep[] | null,
): BookingActionDef[] {
  if (!user) return [];
  const next = getNextPendingApprovalStep(approvalSteps);
  return actions.filter((action) => {
    if (action.id === "approve" && next) {
      return canActOnApprovalStep(user, next, approvalSteps);
    }
    const perm = ACTION_PERMISSION[action.id];
    return perm ? hasPermission(user, perm) : true;
  });
}

/** Actions available for the current booking status (detail or list). */
export function getBookingActions(
  ctx: BookingActionContext,
  user?: AuthUser | null,
): BookingActionDef[] {
  const { status, approvalSteps } = ctx;

  let actions: BookingActionDef[];

  switch (status) {
    case "SUBMITTED":
      actions = withCancel(SUBMITTED_ACTIONS);
      break;
    case "PENDING_APPROVAL":
    case "APPROVED_PENDING_SIGNATURE":
      actions = withCancel(approvalActions(approvalSteps));
      break;
    case "APPROVED":
      actions = [{ ...VIEW_CONTRACT_ACTION, primary: true }, CANCEL_ACTION];
      break;
    case "CONTRACT_READY":
      actions = [{ ...VIEW_CONTRACT_ACTION, primary: true }];
      break;
    case "SIGNED_CUSTOMER":
      actions = [SIGN_CONTRACT_STAFF_ACTION, VIEW_CONTRACT_ACTION];
      break;
    case "FULLY_EXECUTED":
      actions = [
        {
          ...VIEW_CONTRACT_ACTION,
          label: "View executed contract",
          primary: true,
        },
      ];
      break;
    case "OPERATION_REQUEST_PENDING":
      actions = withCancel(OPERATION_REVIEW_ACTIONS);
      break;
    case "PAID":
      if (
        canAllocateBooking({ status, schedulingStatus: ctx.schedulingStatus })
      ) {
        actions = [
          {
            id: "allocateBooking",
            label: "Allocate booking",
            shortLabel: "Allocate",
            description: "Assign to train, wagons, and finalize schedule",
            confirmTitle: "Allocate booking?",
            confirmDescription: "Opens the train allocation wizard.",
            variant: "default",
            icon: TrainTrack,
            primary: true,
          },
          {
            id: "startTransit",
            label: "Start transit",
            shortLabel: "Transit",
            description: "Begin rail movement",
            confirmTitle: "Start transit?",
            confirmDescription: "The booking will move to in transit status.",
            variant: "default",
            icon: Truck,
          },
        ];
      } else {
        actions = [
          {
            id: "startTransit",
            label: "Start transit",
            shortLabel: "Transit",
            description: "Begin rail movement",
            confirmTitle: "Start transit?",
            confirmDescription: "The booking will move to in transit status.",
            variant: "default",
            icon: Truck,
            primary: true,
          },
        ];
      }
      break;
    case "IN_TRANSIT":
      actions = [
        {
          id: "complete",
          label: "Complete booking",
          shortLabel: "Complete",
          description: "Mark journey finished",
          confirmTitle: "Complete booking?",
          confirmDescription:
            "Marks the booking as completed. No further staff transitions apply.",
          variant: "default",
          icon: Play,
          primary: true,
        },
      ];
      break;
    case "CHANGES_REQUESTED":
      actions = [CANCEL_ACTION];
      break;
    case "PENDING_CONSOLIDATION":
      // View-only while waiting for a consolidation partner; cancel still allowed.
      actions = [CANCEL_ACTION];
      break;
    default:
      actions = [];
  }

  if (user === undefined) return actions;
  return filterActionsByUser(actions, user, approvalSteps);
}

/** Opens contract page without confirmation dialog. */
export function isContractNavAction(id: BookingActionId): boolean {
  return id === "viewContract" || id === "signContractStaff";
}

/** Opens allocation wizard without confirmation dialog. */
export function isAllocateAction(id: BookingActionId): boolean {
  return id === "allocateBooking";
}

export function listRowHasActions(
  row: {
    status: BookingStatus;
    paymentCurrency: string;
    approvalSteps?: BookingApprovalStep[] | null;
  },
  user?: AuthUser | null,
): boolean {
  const actions = getBookingActions(
    {
      status: row.status,
      paymentCurrency: row.paymentCurrency,
      reference: "",
      approvalSteps: row.approvalSteps ?? undefined,
      schedulingStatus: row.status,
    },
    user,
  );
  return actions.length > 0;
}
