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

import { URL_CONSTANTS } from "@/constants/URLS";
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;
  }>;
}

export interface PriceLineItem {
  code: string;
  description: string;
  amount: number;
  currency: string;
}

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

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

export interface BookingListFilter {
  status?: 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;
  },
  create: async (payload: CreateBookingPayload): Promise<Freight.IBooking> => {
    const { data } = await client.post("/api/bookings", payload);
    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>,
  ): Promise<{ booking: Freight.IBooking; warnings: string[] }> => {
    const { data } = await client.patch(`/api/bookings/${id}`, payload);
    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;
  },

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

  submit: async (id: string): Promise<Freight.IBooking> => {
    const { data } = await client.post(`/api/bookings/${id}/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;
  },

  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;
  },

  pay: async (id: string): Promise<{ redirectUrl: string }> => {
    const { data } = await client.post(`/api/bookings/${id}/payment/pay`);
    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;
  },
};
