import type { Freight, PaginatedResponse } from "@edr/types";

import { URL_CONSTANTS } from "@/constants/URLS";
import type { BookingDocuments } from "@/pages/bookings/new-booking-form/schema";
import { buildBookingFormData } from "./booking-form-data";
import { client } from "../utils/api";

const B = URL_CONSTANTS.BOOKINGS;

export type CreateBookingPayload = Freight.CreateBookingDto;

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 PriceLineItem {
  code: string;
  description: string;
  /** Computed line total (unitAmount × quantity). */
  amount: number;
  /** Price for a single unit of this charge (e.g. one 20ft container, one ton). */
  unitAmount?: number;
  /** Unit the rate is charged per: PER_CONTAINER | PER_TON | PER_WAGON | PER_KM | FLAT. */
  unit?: string;
  /** How many units this charge applies to (containers, tons, wagons; 1 for FLAT). */
  quantity?: number;
  currency: string;
}

export interface GeneratePriceResponse {
  bookingId: string;
  totalAmount: number;
  currency: string;
  lineItems: PriceLineItem[];
  warnings: string[];
}

export interface SubmitBookingResponse {
  bookingId: string;
  status: string;
  priceChanged: boolean;
  previousTotalAmount?: number;
  totalAmount: number;
  currency: string;
  lineItems?: PriceLineItem[];
  message?: string;
}

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

export interface BookingListFilter {
  status?: string;
  /** Comma-separated statuses (overrides `status` when set). */
  statuses?: string;
  /** ONE_TIME or GENERAL_CONTRACT. */
  bookingType?: string;
  /** CONTAINER or BULK. */
  freightType?: string;
  /** IMPORT / EXPORT / DOMESTIC. */
  tradeDirection?: string;
  /** Narrow to a single operational profile (importer/exporter/freight_forwarder). */
  companyProfileId?: string;
  /** Created-date range (ISO). */
  createdFrom?: string;
  createdTo?: string;
  page?: number;
  pageSize?: number;
  sortBy?: string;
  sortOrder?: "ASC" | "DESC";
}

export const bookingsService = {
  list: async (
    filter: BookingListFilter | void = {},
  ): Promise<PaginatedResponse<Freight.IBooking>> => {
    const { data } = await client.get("/api/bookings", { params: filter });
    return data.data;
  },
  get: async (id: string): Promise<Freight.IBooking> => {
    const { data } = await client.get(`/api/bookings/${id}`);
    return data.data;
  },
  tracking: async (id: string): Promise<Freight.IBookingTracking> => {
    const { data } = await client.get(`/api/bookings/${id}/tracking`);
    return data.data;
  },
  create: async (
    payload: CreateBookingPayload,
    documents?: BookingDocuments,
  ): Promise<Freight.IBooking> => {
    const formData = buildBookingFormData(payload, documents);
    const { data } = await client.post("/api/bookings", formData, {
      headers: { "Content-Type": "multipart/form-data" },
    });
    return data.data.booking;
  },
  getReferenceData: async (): Promise<Freight.BookingReferenceData> => {
    const { data } = await client.get("/api/bookings/reference-data");
    return data.data;
  },
  update: async (
    id: string,
    payload: Partial<CreateBookingPayload>,
    documents?: BookingDocuments,
  ): Promise<{ booking: Freight.IBooking; warnings: string[] }> => {
    const formData = buildBookingFormData(payload, documents);
    const { data } = await client.patch(`/api/bookings/${id}`, formData, {
      headers: { "Content-Type": "multipart/form-data" },
    });
    return data.data;
  },

  remove: async (id: string): Promise<void> => {
    await client.delete(`/api/bookings/${id}`);
  },

  cancel: async (id: string, reason: string): Promise<Freight.IBooking> => {
    const { data } = await client.post(`/api/bookings/${id}/cancel`, { reason });
    return data.data;
  },

  reject: async (id: string, reason?: string): Promise<Freight.IBooking> => {
    const { data } = await client.post(`/api/bookings/${id}/reject`, { reason });
    return data.data;
  },

  generatePrice: async (id: string): Promise<GeneratePriceResponse> => {
    const { data } = await client.post(`/api/bookings/${id}/generate-price`);
    return data.data;
  },

  submit: async (id: string): Promise<SubmitBookingResponse> => {
    const { data } = await client.post(`/api/bookings/${id}/submit`);
    return data.data;
  },

  confirmSubmit: async (id: string): Promise<SubmitBookingResponse> => {
    const { data } = await client.post(`/api/bookings/${id}/confirm-submit`);
    return data.data;
  },

  uploadDocuments: async (
    id: string,
    files: Record<string, File | File[] | null>,
  ): Promise<Freight.IBooking> => {
    const formData = new FormData();
    for (const [key, fileOrFiles] of Object.entries(files)) {
      if (!fileOrFiles) continue;
      if (Array.isArray(fileOrFiles)) {
        for (const f of fileOrFiles) formData.append(key, f);
      } else {
        formData.append(key, fileOrFiles);
      }
    }
    const { data } = await client.post(
      `/api/bookings/${id}/documents`,
      formData,
      { headers: { "Content-Type": "multipart/form-data" } },
    );
    return data.data;
  },

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

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

  proceedToOperation: async (id: string): Promise<Freight.IBooking> => {
    const { data } = await client.post(`/api/bookings/${id}/clearance/proceed`);
    return data.data;
  },

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

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

  checkPayment: async (orderId: string): Promise<{ status: string }> => {
    const { data } = await client.post(`/api/payments/bookings/check-payment/${orderId}`);
    return data.data ?? data;
  },

  signContract: async (
    id: string,
    payload: SignContractPayload,
  ): Promise<Freight.IBooking> => {
    const { data } = await client.post(B.CONTRACT_SIGN(id), payload);
    return data.data ?? data;
  },

  getBookableSchedules: async (
    query: Freight.BookableSchedulesQuery = {},
  ): Promise<Freight.BookableScheduleItem[]> => {
    const { data } = await client.get(
      URL_CONSTANTS.TRAIN_SCHEDULING.BOOKABLE_SCHEDULES,
      { params: query },
    );
    return data.data;
  },

  /**
   * Day-level pool: the days that have a departure on the route. The customer
   * picks a day; the engine assigns the train. No capacity is returned.
   */
  getAvailableDays: async (
    query: Freight.AvailableDaysQuery = {},
  ): Promise<string[]> => {
    const { data } = await client.get(
      URL_CONSTANTS.TRAIN_SCHEDULING.AVAILABLE_DAYS,
      { params: query },
    );
    return (data.data as Freight.AvailableDaysResponse).days;
  },
};
