import { api as client } from "../auth/http";
import { unwrap } from "@/utils/endpoint";
import { URL_CONSTANTS } from "@/constants/URLS";
import type { BookingDetail } from "@/types/booking";
import type { Freight } from "@edr/types";

const B = URL_CONSTANTS.BOOKINGS;

export interface BookingListFilter {
  status?: string;
  /** Comma-separated statuses for grouped tabs */
  statuses?: string;
  schedulingStatuses?: string;
  assignedToSchedule?: "true" | "false";
  /** Tab key for React Query cache (not sent to API) */
  tab?: string;
  // customerId?: string;
  companyId?: string;
  freightType?: string;
  tradeDirection?: string;
  paymentCurrency?: string;
  page?: number;
  pageSize?: number;
  sortBy?: string;
  sortOrder?: "ASC" | "DESC";
}

export interface PaginatedBookings {
  items: BookingDetail[];
  total: number;
}

export interface BookingListSummaryMetrics {
  inQueue: number;
  onThisPage: number;
  needsAction: number;
  urgent: number;
}

export interface BookingListSummaryTabs {
  all: number;
  intake: number;
  in_approval: number;
  approved_contract: number;
  payment: number;
  operations: number;
  completed: number;
  closed: number;
}

export interface BookingListSummary {
  metrics: BookingListSummaryMetrics;
  tabs: BookingListSummaryTabs;
}

export interface ApproveStepPayload {
  id: string;
  stepId: string;
  requiredRole: string;
}

export interface RejectStepPayload {
  id: string;
  stepId: string;
  reason: string;
}

export interface ContractView {
  bookingId: string;
  reference: string;
  status: string;
  templateKey: string;
  title: string;
  html: string;
  canSignCustomer: boolean;
  canSignStaff: boolean;
  hasContractDocument: boolean;
  signatures: Array<{
    role: string;
    signerDisplayName: string;
    signedAt: string;
    signatureImageUrl?: string | null;
  }>;
  /** Current viewer's reusable saved signature, if they have one. */
  savedSignature?: {
    signerDisplayName: string;
    signatureImageUrl?: string | null;
  } | null;
}

export interface ConsolidationWagonSlot {
  containerTypeCode: string;
  quantity: number;
  containersPerWagon: number;
  remainder: number;
  slotsNeeded: number;
}

export interface ConsolidationDetails {
  statusMessage: string;
  wagonSlots: ConsolidationWagonSlot[];
  partner: { id: string; reference: string } | null;
  splitBilling: { bookingShare: number; partnerShare: number } | null;
}

export interface SignContractPayload {
  role: "CUSTOMER" | "STAFF";
  signatureImageBase64: string;
  signerDisplayName: string;
  consentText?: string;
}

async function postBooking<T>(url: string, body?: unknown): Promise<T> {
  const response = await client.post<T>(url, body ?? {});
  return unwrap(response.data);
}

export const bookingsService = {
  getListSummary: async (filter?: BookingListFilter): Promise<BookingListSummary> => {
    const params: Record<string, string | number | boolean | undefined> = {};
    if (filter) {
      if (filter.statuses) params.statuses = filter.statuses;
      else if (filter.status) params.status = filter.status;
      if (filter.page != null) params.page = filter.page;
      if (filter.pageSize != null) params.pageSize = filter.pageSize;
      if (filter.companyId) params.companyId = filter.companyId;
      if (filter.freightType) params.freightType = filter.freightType;
      if (filter.tradeDirection) params.tradeDirection = filter.tradeDirection;
      if (filter.paymentCurrency) params.paymentCurrency = filter.paymentCurrency;
    }
    const response = await client.get<BookingListSummary>(B.LIST_SUMMARY, {
      params,
    });
    return unwrap(response.data) as BookingListSummary;
  },

  list: async (filter?: BookingListFilter): Promise<PaginatedBookings> => {
    const params: Record<string, string | number | boolean | undefined> = {};
    if (filter) {
      if (filter.statuses) params.statuses = filter.statuses;
      else if (filter.status) params.status = filter.status;
      // filter.tab is intentionally omitted from API params
      if (filter.page != null) params.page = filter.page;
      if (filter.pageSize != null) params.pageSize = filter.pageSize;
      if (filter.sortBy) params.sortBy = filter.sortBy;
      if (filter.sortOrder) params.sortOrder = filter.sortOrder;
      if (filter.schedulingStatuses) params.schedulingStatuses = filter.schedulingStatuses;
      if (filter.assignedToSchedule) params.assignedToSchedule = filter.assignedToSchedule;
      if (filter.companyId) params.companyId = filter.companyId;
      if (filter.freightType) params.freightType = filter.freightType;
      if (filter.tradeDirection) params.tradeDirection = filter.tradeDirection;
      if (filter.paymentCurrency) params.paymentCurrency = filter.paymentCurrency;
    }
    const response = await client.get<PaginatedBookings>(B.BASE, {
      params,
    });
    const data = unwrap(response.data);
    return {
      items: (data.items ?? []) as BookingDetail[],
      total: data.total ?? 0,
    };
  },

  getById: async (id: string): Promise<BookingDetail> => {
    const response = await client.get<BookingDetail>(B.BY_ID(id));
    return unwrap(response.data) as BookingDetail;
  },

  remove: async (id: string): Promise<void> => {
    await client.delete(B.BY_ID(id));
  },

  // ── Document clearance (GL workflow) ──
  getClearance: async (id: string): Promise<Freight.ClearanceView> => {
    const response = await client.get(`/bookings/${id}/clearance`);
    return unwrap(response.data) as Freight.ClearanceView;
  },

  reviewClearanceDocument: (
    id: string,
    payload: { fileKey: string; status: "APPROVED" | "QUERIED"; note?: string },
  ) => postBooking<BookingDetail>(`/bookings/${id}/clearance/review`, payload),

  uploadClearanceOutput: async (
    id: string,
    files: Record<string, File | null>,
  ): Promise<BookingDetail> => {
    const form = new FormData();
    for (const [key, file] of Object.entries(files)) {
      if (file) form.append(key, file);
    }
    const response = await client.post(
      `/bookings/${id}/clearance/output-documents`,
      form,
      { headers: { "Content-Type": "multipart/form-data" } },
    );
    return unwrap(response.data) as BookingDetail;
  },

  finalizeClearance: (id: string) =>
    postBooking<BookingDetail>(`/bookings/${id}/clearance/finalize`),

  staffAccept: (id: string, validityDays: number) =>
    postBooking<BookingDetail>(B.STAFF_ACCEPT(id), { validityDays }),

  requestChanges: (id: string, note: string) =>
    postBooking<BookingDetail>(B.STAFF_REQUEST_CHANGES(id), { note }),

  staffReject: (id: string, reason: string) =>
    postBooking<BookingDetail>(B.STAFF_REJECT(id), { reason }),

  /** Marketing/operations review of a drawdown order's operation request. */
  reviewOperation: (
    id: string,
    decision: "ACCEPT" | "REQUEST_CHANGES" | "ADJUST_PRICE",
    options: { note?: string; amount?: number } = {},
  ) =>
    postBooking<BookingDetail>(`/bookings/${id}/operation/review`, {
      decision,
      ...options,
    }),

  /** Adjust a booking's total price (pass null amount to clear the adjustment). */
  adjustPrice: (id: string, amount: number | null, reason?: string) =>
    postBooking<BookingDetail>(`/bookings/${id}/adjust-price`, {
      amount,
      reason,
    }),

  approveStep: ({ id, stepId, requiredRole }: ApproveStepPayload) =>
    postBooking<BookingDetail>(B.APPROVE_STEP(id, stepId), { requiredRole }),

  rejectStep: ({ id, stepId, reason }: RejectStepPayload) =>
    postBooking<BookingDetail>(B.REJECT_STEP(id, stepId), { reason }),

  generateContract: (id: string) =>
    postBooking<BookingDetail>(B.CONTRACT_GENERATE(id)),

  getContractView: async (id: string): Promise<ContractView> => {
    const response = await client.get<ContractView>(B.CONTRACT_VIEW(id));
    return unwrap(response.data) as ContractView;
  },

  downloadContract: async (id: string): Promise<Blob> => {
    const response = await client.get(B.CONTRACT_DOWNLOAD(id), {
      responseType: "blob",
    });
    return ensurePdfBlob(response.data as Blob);
  },

  downloadContractDocument: async (id: string): Promise<Blob> => {
    const response = await client.get(B.CONTRACT_DOCUMENT(id), {
      responseType: "blob",
    });
    return ensurePdfBlob(response.data as Blob);
  },

  signContract: (id: string, payload: SignContractPayload) =>
    postBooking<BookingDetail>(B.CONTRACT_SIGN(id), payload),

  getSummary: async (id: string): Promise<{ summary: string }> => {
    const response = await client.get<{ summary: string }>(B.SUMMARY(id));
    return unwrap(response.data);
  },

  getConsolidationDetails: async (
    id: string,
  ): Promise<ConsolidationDetails> => {
    const response = await client.get<ConsolidationDetails>(B.CONSOLIDATION(id));
    return unwrap(response.data) as ConsolidationDetails;
  },

  customerSign: (id: string, payload: SignContractPayload) =>
    postBooking<BookingDetail>(B.CUSTOMER_SIGN(id), {
      ...payload,
      role: "CUSTOMER",
    }),

  marketingApprove: (id: string, payload: SignContractPayload) =>
    postBooking<BookingDetail>(B.MARKETING_APPROVE(id), {
      ...payload,
      role: "STAFF",
    }),

  payBooking: (id: string) => postBooking<BookingDetail>(B.PAYMENT_PAY(id)),

  startTransit: (id: string) =>
    postBooking<BookingDetail>(B.START_TRANSIT(id)),

  complete: (id: string) => postBooking<BookingDetail>(B.COMPLETE(id)),

  cancel: (id: string, reason: string) =>
    postBooking<BookingDetail>(B.CANCEL(id), { reason }),

  create: async (payload: Record<string, unknown>): Promise<BookingDetail> => {
    const response = await client.post<{ booking: BookingDetail } | BookingDetail>(
      B.BASE,
      payload,
    );
    const data = unwrap(response.data) as { booking?: BookingDetail };
    return (data.booking ?? data) as BookingDetail;
  },

  getReferenceData: async () => {
    const response = await client.get(B.REFERENCE_DATA);
    return unwrap(response.data);
  },

  governmentExpedite: (id: string) =>
    postBooking<BookingDetail>(B.GOVERNMENT_EXPEDITE(id)),
};

async function ensurePdfBlob(blob: Blob): Promise<Blob> {
  if (blob.type.includes("application/json")) {
    const body = JSON.parse(await blob.text()) as { message?: string };
    throw new Error(body.message ?? "Contract PDF download failed");
  }
  return blob;
}
