import type { Freight, PaginatedResponse } from "@edr/types";
import { endpoint } from "@/utils/endpoint";
import type {
  CreateFileUploadFieldDto,
  FileUploadField,
  FileUploadSetting,
  UpdateFileUploadFieldDto,
} from "@/types/fileUploadSettings";
import {
  bookingsService,
  type ApproveDeliveryResponse,
  BookingListFilter,
  CreateBookingPayload,
  type CustomerTruckAssignmentPayload,
  GeneratePriceResponse,
  type MyBookingWindow,
  SubmitBookingResponse,
} from "./bookings.service";
import {
  contractsService,
  ContractListFilter,
  CreateContractPayload,
  UpdateContractPayload,
  ContractDocuments,
  GenerateContractPriceResponse,
  SubmitContractResponse,
  ShipmentValidation,
} from "./contracts.service";
import type { BookingDocuments } from "@/pages/bookings/new-booking-form/schema";
import {
  paymentsService,
  InitiatePaymentPayload,
  InitiateResponse,
  IntentStatus,
} from "./payments.service";
import { consignmentsService } from "./consignments.service";
import {
  invoicesService,
  PortalInvoice,
  PortalInvoiceDetail,
  PayInvoicePayload,
} from "./invoices.service";
import { trackingService } from "./tracking.service";
import { fileUploadSettingsService } from "./fileUploadSettings.service";
import { dropdownSettingsService } from "./dropdownSettings.service";
import { authService } from "./auth.service";
import { companiesService } from "./companies.service";
import {
  CreateDropdownOptionDto,
  CreateDropdownSettingDto,
  DropdownOption,
  DropdownSetting,
  UpdateDropdownOptionDto,
  UpdateDropdownSettingDto,
} from "@/types/dropdownSettings";
import type {
  ChangeRequestResponse,
  CompanyDocument,
  CompanyInfoResponse,
  CompanyNationality,
  CompanyProfileResponse,
  LicenseFile,
  CreateCompanyPayload,
  DashboardSummary,
  OnboardingRequirements,
  ProfileTypeValue,
} from "./companies.service";
import type { ProfileResponse, UpdateProfilePayload } from "@/types/profile";
import type {
  AuthUser,
  ChangePasswordPayload,
  CheckAvailabilityPayload,
  CheckAvailabilityResponse,
  GenerateVerificationCodePayload,
  LoginPayload,
  LoginResponse,
  OtpPayload,
  OtpResponse,
  SetPasswordPayload,
  SignupPayload,
  SignupResponse,
  ForgotPasswordRequestPayload,
  ForgotPasswordVerifyPayload,
  ResetLinkAccount,
  ResetTicket,
  ResolveResetLinkPayload,
  SendContactOtpPayload,
  SendContactOtpResponse,
  UpdateAccountNamePayload,
  UpdateContactPayload,
  UpdateContactResponse,
} from "@/types/auth";

// ---------------------------------------------------------------------------
// API definition
// ---------------------------------------------------------------------------

export const api = {
  auth: {
    login: endpoint<LoginPayload, LoginResponse>(
      "auth",
      "login",
      authService.login,
    ),
    createUser: endpoint<SignupPayload, SignupResponse>(
      "auth",
      "createUser",
      authService.createUser,
    ),
    getMyInfo: endpoint<void, AuthUser>(
      "auth",
      "getMyInfo",
      authService.getMyInfo,
    ),
    generateVerificationCode: endpoint<GenerateVerificationCodePayload, string>(
      "auth",
      "generateVerificationCode",
      authService.generateVerificationCode,
    ),
    setPassword: endpoint<SetPasswordPayload, void>(
      "auth",
      "setPassword",
      authService.setPassword,
    ),
    requestPasswordReset: endpoint<ForgotPasswordRequestPayload, void>(
      "auth",
      "requestPasswordReset",
      authService.requestPasswordReset,
    ),
    verifyPasswordResetOtp: endpoint<ForgotPasswordVerifyPayload, ResetTicket>(
      "auth",
      "verifyPasswordResetOtp",
      authService.verifyPasswordResetOtp,
    ),
    resolveResetLink: endpoint<ResolveResetLinkPayload, ResetLinkAccount>(
      "auth",
      "resolveResetLink",
      authService.resolveResetLink,
    ),
    resetPassword: endpoint<SetPasswordPayload, void>(
      "auth",
      "resetPassword",
      authService.resetPassword,
    ),
    checkAvailability: endpoint<CheckAvailabilityPayload, CheckAvailabilityResponse>(
      "auth",
      "checkAvailability",
      authService.checkAvailability,
    ),
    sendOTP: endpoint<OtpPayload, OtpResponse>(
      "auth",
      "sendOTP",
      authService.sendOTP,
    ),
    verifyOTP: endpoint<OtpPayload, OtpResponse>(
      "auth",
      "verifyOTP",
      authService.verifyOTP,
    ),
    changePassword: endpoint<ChangePasswordPayload, void>(
      "auth",
      "changePassword",
      authService.changePassword,
    ),
    logout: endpoint<void, void>("auth", "logout", authService.logout),
  },

  // The signed-in user's own IAM account — the phone/email that OTPs and SMS
  // actually go to. Separate from `companies`, which is business profile data.
  account: {
    sendContactOtp: endpoint<SendContactOtpPayload, SendContactOtpResponse>(
      "account",
      "sendContactOtp",
      authService.sendContactOtp,
    ),
    updateContact: endpoint<UpdateContactPayload, UpdateContactResponse>(
      "account",
      "updateContact",
      authService.updateContact,
    ),
    updateName: endpoint<UpdateAccountNamePayload, { success: true }>(
      "account",
      "updateName",
      authService.updateAccountName,
    ),
  },

  companies: {
    getInfo: endpoint<void, CompanyInfoResponse | null>(
      "companies",
      "getInfo",
      companiesService.getInfo,
    ),

    create: endpoint<CreateCompanyPayload, CompanyInfoResponse>(
      "companies",
      "create",
      companiesService.create,
    ),

    getProfile: endpoint<void, ProfileResponse>(
      "companies",
      "getProfile",
      companiesService.getProfile,
    ),

    updateProfile: endpoint<UpdateProfilePayload, ProfileResponse>(
      "companies",
      "updateProfile",
      companiesService.updateProfile,
    ),

    getDashboard: endpoint<string | undefined, DashboardSummary>(
      "companies",
      "getDashboard",
      companiesService.getDashboard,
    ),

    addCompanyProfiles: endpoint<{ types: string[] }, CompanyProfileResponse[]>(
      "companies",
      "addCompanyProfiles",
      companiesService.addCompanyProfiles,
    ),

    createCompanyProfile: endpoint<
      { type: ProfileTypeValue; businessLicense?: string },
      CompanyProfileResponse
    >(
      "companies",
      "createCompanyProfile",
      companiesService.createCompanyProfile,
    ),

    startOnboarding: endpoint<
      {
        companyType: string;
        roles: ProfileTypeValue[];
        nationality?: CompanyNationality;
      },
      CompanyInfoResponse
    >("companies", "startOnboarding", companiesService.startOnboarding),

    setOnboardingStep: endpoint<{ step: string }, void>(
      "companies",
      "setOnboardingStep",
      companiesService.setOnboardingStep,
    ),

    completeOnboarding: endpoint<void, CompanyInfoResponse>(
      "companies",
      "completeOnboarding",
      companiesService.completeOnboarding,
    ),

    onboardingRequirements: endpoint<void, OnboardingRequirements>(
      "companies",
      "onboardingRequirements",
      companiesService.getOnboardingRequirements,
    ),

    documents: endpoint<{ companyId: string }, CompanyDocument[]>(
      "companies",
      "documents",
      ({ companyId }) => companiesService.getDocuments(companyId),
    ),

    poaDelegation: endpoint<void, LicenseFile[]>(
      "companies",
      "poaDelegation",
      companiesService.getPoaDelegation,
    ),

    changeRequest: endpoint<void, ChangeRequestResponse | null>(
      "companies",
      "changeRequest",
      companiesService.getChangeRequest,
    ),

    reapplyProfile: endpoint<{ profileId: string }, CompanyProfileResponse>(
      "companies",
      "reapplyProfile",
      ({ profileId }) => companiesService.reapplyProfile(profileId),
    ),
  },

  bookings: {
    list: endpoint<
      BookingListFilter | void,
      PaginatedResponse<Freight.IBooking>
    >("bookings", "list", bookingsService.list),

    get: endpoint<{ id: string }, Freight.IBooking>(
      "bookings",
      "get",
      ({ id }) => bookingsService.get(id),
    ),

    tracking: endpoint<{ id: string }, Freight.IBookingTracking>(
      "bookings",
      "tracking",
      ({ id }) => bookingsService.tracking(id),
    ),

    assignCustomerTruck: endpoint<
      { id: string; payload: CustomerTruckAssignmentPayload },
      Freight.IBooking
    >("bookings", "assignCustomerTruck", ({ id, payload }) =>
      bookingsService.assignCustomerTruck(id, payload),
    ),

    downloadCustomerTruckFreightOrder: endpoint<{ id: string; copies?: number[] }, Blob>(
      "bookings",
      "downloadCustomerTruckFreightOrder",
      ({ id, copies }) => bookingsService.downloadCustomerTruckFreightOrder(id, copies),
    ),

    downloadHandoverDocument: endpoint<{ inventoryId: string }, Blob>(
      "bookings",
      "downloadHandoverDocument",
      ({ inventoryId }) =>
        bookingsService.downloadHandoverDocument(inventoryId),
    ),

    downloadBookingHandoverDocument: endpoint<{ bookingId: string }, Blob>(
      "bookings",
      "downloadBookingHandoverDocument",
      ({ bookingId }) =>
        bookingsService.downloadBookingHandoverDocument(bookingId),
    ),

    create: endpoint<
      { payload: CreateBookingPayload; documents?: BookingDocuments },
      Freight.IBooking
    >("bookings", "create", ({ payload, documents }) =>
      bookingsService.create(payload, documents),
    ),

    update: endpoint<
      {
        id: string;
        dto: Partial<CreateBookingPayload>;
        documents?: BookingDocuments;
      },
      { booking: Freight.IBooking; warnings: string[] }
    >("bookings", "update", ({ id, dto, documents }) =>
      bookingsService.update(id, dto, documents),
    ),

    referenceData: endpoint<void, Freight.BookingReferenceData>(
      "bookings",
      "referenceData",
      bookingsService.getReferenceData,
    ),

    remove: endpoint<{ id: string }, void>("bookings", "remove", ({ id }) =>
      bookingsService.remove(id),
    ),

    cancel: endpoint<{ id: string; reason: string }, Freight.IBooking>(
      "bookings",
      "cancel",
      ({ id, reason }) => bookingsService.cancel(id, reason),
    ),

    reject: endpoint<{ id: string; reason?: string }, Freight.IBooking>(
      "bookings",
      "reject",
      ({ id, reason }) => bookingsService.reject(id, reason),
    ),

    generatePrice: endpoint<{ id: string }, GeneratePriceResponse>(
      "bookings",
      "generatePrice",
      ({ id }) => bookingsService.generatePrice(id),
    ),

    submit: endpoint<{ id: string }, SubmitBookingResponse>(
      "bookings",
      "submit",
      ({ id }) => bookingsService.submit(id),
    ),

    confirmSubmit: endpoint<{ id: string }, SubmitBookingResponse>(
      "bookings",
      "confirmSubmit",
      ({ id }) => bookingsService.confirmSubmit(id),
    ),

    uploadDocuments: endpoint<
      { id: string; files: Record<string, File | File[] | null> },
      Freight.IBooking
    >("bookings", "uploadDocuments", ({ id, files }) =>
      bookingsService.uploadDocuments(id, files),
    ),

    getClearance: endpoint<{ id: string }, Freight.ClearanceView>(
      "bookings",
      "getClearance",
      ({ id }) => bookingsService.getClearance(id),
    ),

    submitClearanceDocuments: endpoint<
      { id: string; files: Record<string, File | null> },
      Freight.IBooking
    >("bookings", "submitClearanceDocuments", ({ id, files }) =>
      bookingsService.submitClearanceDocuments(id, files),
    ),

    proceedToOperation: endpoint<
      { id: string; scheduledDate: string },
      Freight.IBooking
    >("bookings", "proceedToOperation", ({ id, scheduledDate }) =>
      bookingsService.proceedToOperation(id, scheduledDate),
    ),

    checkPayment: endpoint<{ orderId: string }, { status: string }>(
      "bookings",
      "checkPayment",
      ({ orderId }) => bookingsService.checkPayment(orderId),
    ),

    approveDelivery: endpoint<{ id: string; signerName: string }, ApproveDeliveryResponse>(
      "bookings",
      "approveDelivery",
      ({ id, signerName }) => bookingsService.approveDelivery(id, signerName),
    ),

    getBookableSchedules: endpoint<
      { originYardId?: string; destinationYardId?: string },
      Freight.BookableScheduleItem[]
    >(
      "train-scheduling",
      "bookableSchedules",
      ({ originYardId, destinationYardId }) =>
        bookingsService.getBookableSchedules({
          originYardId,
          destinationYardId,
        }),
    ),

    getAvailableDays: endpoint<
      { originYardId?: string; destinationYardId?: string },
      string[]
    >(
      "train-scheduling",
      "availableDays",
      ({ originYardId, destinationYardId }) =>
        bookingsService.getAvailableDays({ originYardId, destinationYardId }),
    ),

    getAvailableDaysForCargo: endpoint<
      Freight.AvailableDaysForCargoQuery,
      string[]
    >("train-scheduling", "availableDaysForCargo", (input) =>
      bookingsService.getAvailableDaysForCargo(input),
    ),

    getAvailableDaysForBooking: endpoint<{ bookingId: string }, string[]>(
      "train-scheduling",
      "availableDaysForBooking",
      ({ bookingId }) => bookingsService.getAvailableDaysForBooking(bookingId),
    ),

    getDayAvailability: endpoint<
      { bookingId: string; date: string },
      Freight.DayAvailabilityResponse
    >("train-scheduling", "dayAvailability", ({ bookingId, date }) =>
      bookingsService.getDayAvailability(bookingId, date),
    ),

    getMyBookingWindows: endpoint<void, MyBookingWindow[]>(
      "train-scheduling",
      "myBookingWindows",
      () => bookingsService.getMyBookingWindows(),
    ),

    getContractBookingWindows: endpoint<{ contractId: string }, MyBookingWindow[]>(
      "train-scheduling",
      "contractBookingWindows",
      ({ contractId }) => bookingsService.getContractBookingWindows(contractId),
    ),
  },

  contracts: {
    list: endpoint<
      ContractListFilter | void,
      PaginatedResponse<Freight.IContract>
    >("contracts", "list", contractsService.list),

    listMy: endpoint<
      ContractListFilter | void,
      PaginatedResponse<Freight.IContract>
    >("contracts", "listMy", contractsService.listMy),

    get: endpoint<{ id: string }, Freight.IContract>(
      "contracts",
      "get",
      ({ id }) => contractsService.get(id),
    ),

    create: endpoint<
      { payload: CreateContractPayload; documents?: ContractDocuments },
      Freight.IContract
    >("contracts", "create", ({ payload, documents }) =>
      contractsService.create(payload, documents),
    ),

    update: endpoint<
      {
        id: string;
        dto: UpdateContractPayload;
        documents?: ContractDocuments;
      },
      Freight.IContract
    >("contracts", "update", ({ id, dto, documents }) =>
      contractsService.update(id, dto, documents),
    ),

    remove: endpoint<{ id: string }, void>("contracts", "remove", ({ id }) =>
      contractsService.remove(id),
    ),

    uploadDocuments: endpoint<
      { id: string; files: ContractDocuments },
      Freight.IContract
    >("contracts", "uploadDocuments", ({ id, files }) =>
      contractsService.uploadDocuments(id, files),
    ),

    generatePrice: endpoint<{ id: string }, GenerateContractPriceResponse>(
      "contracts",
      "generatePrice",
      ({ id }) => contractsService.generatePrice(id),
    ),

    submit: endpoint<{ id: string }, SubmitContractResponse>(
      "contracts",
      "submit",
      ({ id }) => contractsService.submit(id),
    ),

    confirmSubmit: endpoint<{ id: string }, SubmitContractResponse>(
      "contracts",
      "confirmSubmit",
      ({ id }) => contractsService.confirmSubmit(id),
    ),

    generateContract: endpoint<{ id: string }, Freight.IContract>(
      "contracts",
      "generateContract",
      ({ id }) => contractsService.generateContract(id),
    ),

    renew: endpoint<
      { id: string; dto: Freight.RenewContractDto },
      Freight.IContract
    >("contracts", "renew", ({ id, dto }) => contractsService.renew(id, dto)),

    getClearance: endpoint<{ id: string }, Freight.ContractClearanceView>(
      "contracts",
      "getClearance",
      ({ id }) => contractsService.getClearance(id),
    ),

    uploadClearanceDocuments: endpoint<
      { id: string; files: Record<string, File | null> },
      Freight.ContractClearanceView
    >("contracts", "uploadClearanceDocuments", ({ id, files }) =>
      contractsService.uploadClearanceDocuments(id, files),
    ),

    createBookingUnderContract: endpoint<
      { id: string; dto: Freight.CreateBookingUnderContractDto },
      Freight.IBooking
    >("contracts", "createBookingUnderContract", ({ id, dto }) =>
      contractsService.createBookingUnderContract(id, dto),
    ),

    initiateBookingUnderContract: endpoint<
      { id: string; contractRouteId?: string },
      Freight.IBooking
    >("contracts", "initiateBookingUnderContract", ({ id, contractRouteId }) =>
      contractsService.initiateBookingUnderContract(id, contractRouteId),
    ),

    completeBookingUnderContract: endpoint<
      {
        id: string;
        bookingId: string;
        dto: Freight.CreateBookingUnderContractDto;
      },
      Freight.IBooking
    >("contracts", "completeBookingUnderContract", ({ id, bookingId, dto }) =>
      contractsService.completeBookingUnderContract(id, bookingId, dto),
    ),

    validateShipment: endpoint<
      { id: string; dto: Freight.CreateBookingUnderContractDto },
      ShipmentValidation
    >("contracts", "validateShipment", ({ id, dto }) =>
      contractsService.validateShipment(id, dto),
    ),

    getContractMilestones: endpoint<
      { id: string },
      Freight.IClearanceMilestone[]
    >("contracts", "getContractMilestones", ({ id }) =>
      contractsService.getContractMilestones(id),
    ),

    getBookingMilestones: endpoint<
      { bookingId: string },
      Freight.IClearanceMilestone[]
    >("contracts", "getBookingMilestones", ({ bookingId }) =>
      contractsService.getBookingMilestones(bookingId),
    ),
  },

  payments: {
    initiate: endpoint<InitiatePaymentPayload, InitiateResponse>(
      "payments",
      "initiate",
      paymentsService.initiate,
    ),

    getIntent: endpoint<{ bookingId: string }, IntentStatus>(
      "payments",
      "getIntent",
      ({ bookingId }) => paymentsService.getIntent(bookingId),
    ),
  },

  consignments: {
    list: endpoint<void, PaginatedResponse<Freight.IConsignment>>(
      "consignments",
      "list",
      consignmentsService.list,
    ),

    get: endpoint<{ id: string }, Freight.IConsignment>(
      "consignments",
      "get",
      ({ id }) => consignmentsService.get(id),
    ),
  },

  tracking: {
    forConsignment: endpoint<
      { consignmentId: string },
      Freight.ITrackingEvent[]
    >("tracking", "forConsignment", ({ consignmentId }) =>
      trackingService.forConsignment(consignmentId),
    ),
  },

  fileUploadSettings: {
    list: endpoint<void, FileUploadSetting[]>(
      "file-upload-settings",
      "list",
      fileUploadSettingsService.list,
    ),

    getById: endpoint<{ id: string }, FileUploadSetting>(
      "file-upload-settings",
      "getById",
      ({ id }) => fileUploadSettingsService.getById(id),
    ),

    getByCode: endpoint<{ code: string }, FileUploadSetting>(
      "file-upload-settings",
      "getByCode",
      ({ code }) => fileUploadSettingsService.getByCode(code),
    ),

    getByEntity: endpoint<{ entity: string }, FileUploadSetting[]>(
      "file-upload-settings",
      "getByEntity",
      ({ entity }) => fileUploadSettingsService.getByEntity(entity),
    ),

    remove: endpoint<{ id: string }, void>(
      "file-upload-settings",
      "remove",
      ({ id }) => fileUploadSettingsService.remove(id),
    ),

    replaceFields: endpoint<
      { id: string; fields: CreateFileUploadFieldDto[] },
      FileUploadField[]
    >("file-upload-settings", "replaceFields", ({ id, fields }) =>
      fileUploadSettingsService.replaceFields(id, fields),
    ),

    addField: endpoint<
      { settingId: string; dto: CreateFileUploadFieldDto },
      FileUploadField
    >("file-upload-settings", "addField", ({ settingId, dto }) =>
      fileUploadSettingsService.addField(settingId, dto),
    ),

    updateField: endpoint<
      { fieldId: string; dto: UpdateFileUploadFieldDto },
      FileUploadField
    >("file-upload-settings", "updateField", ({ fieldId, dto }) =>
      fileUploadSettingsService.updateField(fieldId, dto),
    ),

    removeField: endpoint<{ fieldId: string }, void>(
      "file-upload-settings",
      "removeField",
      ({ fieldId }) => fileUploadSettingsService.removeField(fieldId),
    ),
  },

  dropdownSettings: {
    list: endpoint<void, DropdownSetting[]>(
      "dropdown-settings",
      "list",
      dropdownSettingsService.list,
    ),

    getById: endpoint<{ id: string }, DropdownSetting>(
      "dropdown-settings",
      "getById",
      ({ id }) => dropdownSettingsService.getById(id),
    ),

    getByCode: endpoint<{ code: string }, DropdownSetting>(
      "dropdown-settings",
      "getByCode",
      ({ code }) => dropdownSettingsService.getByCode(code),
    ),

    create: endpoint<CreateDropdownSettingDto, DropdownSetting>(
      "dropdown-settings",
      "create",
      (payload) => dropdownSettingsService.create(payload),
    ),

    update: endpoint<
      { id: string; dto: UpdateDropdownSettingDto },
      DropdownSetting
    >("dropdown-settings", "update", ({ id, dto }) =>
      dropdownSettingsService.update(id, dto),
    ),

    remove: endpoint<{ id: string }, void>(
      "dropdown-settings",
      "remove",
      ({ id }) => dropdownSettingsService.remove(id),
    ),
    replaceOptions: endpoint<
      { id: string; options: CreateDropdownOptionDto[] },
      DropdownOption[]
    >("dropdown-settings", "replaceOptions", ({ id, options }) =>
      dropdownSettingsService.replaceOptions(id, options),
    ),

    addOption: endpoint<
      { id: string; dto: CreateDropdownOptionDto },
      DropdownOption
    >("dropdown-settings", "addOption", ({ id, dto }) =>
      dropdownSettingsService.addOption(id, dto),
    ),

    updateOption: endpoint<
      { optionId: string; dto: UpdateDropdownOptionDto },
      DropdownOption
    >("dropdown-settings", "updateOption", ({ optionId, dto }) =>
      dropdownSettingsService.updateOption(optionId, dto),
    ),

    removeOption: endpoint<{ optionId: string }, void>(
      "dropdown-settings",
      "removeOption",
      ({ optionId }) => dropdownSettingsService.removeOption(optionId),
    ),
  },

  invoices: {
    listMy: endpoint<void, PortalInvoice[]>(
      "invoices",
      "listMy",
      invoicesService.listMy,
    ),

    get: endpoint<{ id: string }, PortalInvoiceDetail>(
      "invoices",
      "get",
      ({ id }) => invoicesService.get(id),
    ),

    pay: endpoint<
      { id: string; payload?: PayInvoicePayload },
      InitiateResponse
    >("invoices", "pay", ({ id, payload }) => invoicesService.pay(id, payload)),
  },
};
