import { client } from "@/utils/api";
import { unwrap } from "@/utils/endpoint";
import { URL_CONSTANTS } from "@/constants/URLS";
import type { ApiResponse } from "@/types/apiResponse";
import type { ProfileResponse, UpdateProfilePayload } from "@/types/profile";
import { isAxiosError } from "axios";

export type ProfileTypeValue =
  | "importer"
  | "exporter"
  | "freight_forwarder"
  | "dj_freight_forwarder"
  | "transporter";

export type CompanyNationality = "ethiopian" | "foreign";

/** Review state of a business-license file (mirrors the API's ProfileLicenseFileView). */
export type LicenseFileStatus = "live" | "pending_add" | "pending_remove";

export interface LicenseFile {
  id: string;
  name: string;
  size: number;
  mimeType: string;
  /** `live` = approved; `pending_add`/`pending_remove` = awaiting backoffice review. */
  status: LicenseFileStatus;
  /**
   * A reviewer's verdict on this specific document. `change_requested` means the
   * customer must upload a corrected version before the role can be approved —
   * orthogonal to `status`, which tracks the staged add/remove workflow.
   */
  reviewStatus?: "change_requested" | "approved" | null;
  /** The reviewer's reason, shown to the customer verbatim. */
  reviewNote?: string | null;
}

export interface ExternalProfileResponse {
  id: string;
  userId: string;
  companyId: string;
  firstName: string;
  lastName: string;
  email: string;
  phone: string | null;
  nationalId: string | null;
  jobTitle: string | null;
  isPrimaryContact: boolean;
  onboardingStep: string | null;
  onboardingCompleted: boolean;
  createdAt: string;
  updatedAt: string;
}

export interface CompanyResponse {
  id: string;
  name: string;
  type: string;
  status: string;
  nationality: CompanyNationality | null;
  tin: string;
  vatNumber: string | null;
  businessLicense: string | null;
  fanNumber: string | null;
  country: string;
  address: string | null;
  phone: string | null;
  email: string | null;
  website: string | null;
  attributes: Record<string, any> | null;
  companyProfiles?: CompanyProfileResponse[];
  createdAt: string;
  updatedAt: string;
}

export interface CompanyProfileResponse {
  id: string;
  type: string;
  reference: string;
  status: string;
  /** @deprecated Superseded by licenseFiles (file model). */
  businessLicense: string | null;
  /** Business-license documents uploaded for this profile. */
  licenseFiles: LicenseFile[];
  attributes: Record<string, any> | null;
  /** Reviewer note when the role is rejected (drives the reapply prompt). */
  reviewNote?: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface CompanyInfoResponse {
  profile: ExternalProfileResponse;
  company: CompanyResponse;
  /**
   * Open profile-edit review, if any. `pending` locks the settings page + new
   * contract/booking creation; `rejected` surfaces the note for reapply.
   */
  review?: {
    status: "pending" | "rejected";
    note: string | null;
  } | null;
}

/** A staged profile-edit review request (portal view). */
export interface ChangeRequestResponse {
  id: string;
  companyId: string;
  status: "pending" | "approved" | "rejected";
  snapshot: Record<string, any>;
  documentFileIds: string[];
  note: string | null;
  submittedAt: string | null;
  reviewedAt: string | null;
  createdAt: string;
  updatedAt: string;
}

/** A single company-level document uploaded against a `file_upload_settings` field. */
export interface CompanyDocument {
  id: string;
  name: string;
  /** The `fileKey` of the setting field it was uploaded against. */
  code: string;
  mimeType: string;
  size: number;
  uploadedAt: string;
  url: string;
  /**
   * `change_requested` means a reviewer has asked for a corrected version of
   * this document; the role cannot be approved until it is re-uploaded.
   */
  reviewStatus?: "change_requested" | "approved" | null;
  /** The reviewer's reason, shown to the customer verbatim. */
  reviewNote?: string | null;
}

/** A single onboarding document field, as resolved and described by the backend. */
export interface OnboardingDocumentField {
  fileKey: string;
  fileLabel: string;
  helpText: string | null;
  isRequired: boolean;
  isMultiple: boolean;
  maxFiles: number;
  allowedExtensions: string[];
  maxSizeMb: number;
  displayOrder: number;
  uploaded: boolean;
}

export interface OnboardingLicenseProfile {
  profileId: string;
  type: string;
  reference: string;
  uploaded: boolean;
}

/** Power of Attorney state — mandatory for freight forwarders, optional otherwise. */
export interface OnboardingPoaState {
  required: boolean;
  provided: boolean;
  delegationLetterUploaded: boolean;
  missingFields: { key: string; label: string }[];
  complete: boolean;
}

/**
 * Server-driven onboarding requirements. The portal renders this verbatim: the
 * backend decides which documents apply (by nationality) and what is still
 * outstanding, so the client never hardcodes required fields or document sets.
 */
export interface OnboardingRequirements {
  documentSettingCode: string;
  nationality: string;
  companyInfo: {
    complete: boolean;
    missingFields: { key: string; label: string }[];
  };
  documents: OnboardingDocumentField[];
  licenseProfiles: OnboardingLicenseProfile[];
  poa: OnboardingPoaState;
  progress: { completed: number; total: number };
  isComplete: boolean;
  onboardingCompleted: boolean;
  outstanding: string[];
}

export interface CompanyProfileInput {
  type:
    | "importer"
    | "exporter"
    | "freight_forwarder"
    | "dj_freight_forwarder"
    | "transporter";
  businessLicense?: string;
}

export interface CreateCompanyPayload {
  companyType?: string;
  nationality?: CompanyNationality;
  companyName: string;
  companyEmail?: string;
  companyPhone?: string;
  companyLocation?: string;
  companyAddress?: string;
  tin?: string;
  vatNumber?: string;
  fanNumber?: string;
  jobTitle?: string;
  isPrimaryContact?: boolean;
  attributes?: Record<string, any>;
  companyProfiles?: CompanyProfileInput[];
}

export interface FreightVolumePoint {
  month: string;
  tonnes: number;
}

export interface DashboardSummary {
  deliveredCount: number;
  completionRate: number;
  spendYtd: number;
  spendCurrency: string;
  spendYtdChangePct: number;
  freightVolume: {
    totalTonnes: number;
    totalValue: number;
    currency: string;
    ytdChangePct: number;
    monthly: FreightVolumePoint[];
  };
}

export const companiesService = {
  getInfo: async (): Promise<CompanyInfoResponse | null> => {
    try {
      const response = await client.get<ApiResponse<CompanyInfoResponse>>(
        URL_CONSTANTS.COMPANIES_API.GET_INFO,
      );
      return unwrap(response.data);
    } catch (e) {
      if (isAxiosError(e) && e.response?.status === 404) {
        return null;
      }
      throw e;
    }
  },

  create: async (
    payload: CreateCompanyPayload,
  ): Promise<CompanyInfoResponse> => {
    const response = await client.post<ApiResponse<CompanyInfoResponse>>(
      URL_CONSTANTS.COMPANIES_API.CREATE,
      payload,
    );
    return unwrap(response.data);
  },

  getProfile: async (): Promise<ProfileResponse> => {
    const response = await client.get<ApiResponse<ProfileResponse>>(
      URL_CONSTANTS.COMPANIES_API.PROFILE,
    );
    return unwrap(response.data);
  },

  updateProfile: async (
    payload: UpdateProfilePayload,
  ): Promise<ProfileResponse> => {
    const response = await client.patch<ApiResponse<ProfileResponse>>(
      URL_CONSTANTS.COMPANIES_API.PROFILE,
      payload,
    );
    return unwrap(response.data);
  },

  getDashboard: async (
    companyProfileId?: string,
  ): Promise<DashboardSummary> => {
    const response = await client.get<ApiResponse<DashboardSummary>>(
      URL_CONSTANTS.COMPANIES_API.DASHBOARD,
      { params: companyProfileId ? { companyProfileId } : undefined },
    );
    return unwrap(response.data);
  },

  addCompanyProfiles: async (payload: {
    types: string[];
  }): Promise<CompanyProfileResponse[]> => {
    const response = await client.post<ApiResponse<CompanyProfileResponse[]>>(
      URL_CONSTANTS.COMPANIES_API.COMPANY_PROFILES,
      payload,
    );
    return unwrap(response.data);
  },

  /** Create a single operational profile and make it the active mode. */
  createCompanyProfile: async (payload: {
    type: ProfileTypeValue;
    businessLicense?: string;
  }): Promise<CompanyProfileResponse> => {
    const response = await client.post<ApiResponse<CompanyProfileResponse>>(
      URL_CONSTANTS.COMPANIES_API.COMPANY_PROFILE,
      payload,
    );
    return unwrap(response.data);
  },

  /** Begin onboarding — create the draft company + profile + role(s) up front. */
  startOnboarding: async (payload: {
    companyType: string;
    roles: ProfileTypeValue[];
    nationality?: CompanyNationality;
  }): Promise<CompanyInfoResponse> => {
    const response = await client.post<ApiResponse<CompanyInfoResponse>>(
      URL_CONSTANTS.COMPANIES_API.ONBOARDING_START,
      payload,
    );
    return unwrap(response.data);
  },

  setOnboardingStep: async (payload: { step: string }): Promise<void> => {
    await client.patch(URL_CONSTANTS.COMPANIES_API.ONBOARDING_STEP, payload);
  },

  completeOnboarding: async (): Promise<CompanyInfoResponse> => {
    const response = await client.post<ApiResponse<CompanyInfoResponse>>(
      URL_CONSTANTS.COMPANIES_API.ONBOARDING_COMPLETE,
    );
    return unwrap(response.data);
  },

  /** Server-driven list of outstanding onboarding requirements + completeness. */
  getOnboardingRequirements: async (): Promise<OnboardingRequirements> => {
    const response = await client.get<ApiResponse<OnboardingRequirements>>(
      URL_CONSTANTS.COMPANIES_API.ONBOARDING_REQUIREMENTS,
    );
    return unwrap(response.data);
  },

  uploadDocuments: async (
    companyId: string,
    files: Record<string, File | File[] | null>,
  ): Promise<void> => {
    const formData = new FormData();
    for (const [fieldName, fileOrFiles] of Object.entries(files)) {
      if (!fileOrFiles) continue;
      if (Array.isArray(fileOrFiles)) {
        for (const f of fileOrFiles) {
          formData.append(fieldName, f);
        }
      } else {
        formData.append(fieldName, fileOrFiles);
      }
    }
    await client.post(
      URL_CONSTANTS.COMPANIES_API.DOCUMENTS(companyId),
      formData,
    );
  },

  /** List documents already uploaded for a company (settings-driven, by fileKey). */
  getDocuments: async (companyId: string): Promise<CompanyDocument[]> => {
    const response = await client.get<ApiResponse<CompanyDocument[]>>(
      URL_CONSTANTS.COMPANIES_API.DOCUMENTS(companyId),
    );
    return unwrap(response.data);
  },

  /**
   * Add business-license document(s) to a company profile. For an approved
   * company the upload is staged for backoffice review; during onboarding it
   * goes live immediately. Returns the profile's full license list with state.
   */
  uploadProfileLicense: async (
    profileId: string,
    files: File[],
    code = "business_license",
  ): Promise<LicenseFile[]> => {
    const formData = new FormData();
    for (const f of files) formData.append(code, f);
    const response = await client.post<ApiResponse<LicenseFile[]>>(
      URL_CONSTANTS.COMPANIES_API.PROFILE_LICENSE(profileId),
      formData,
    );
    return unwrap(response.data);
  },

  /** Replace a license file with a newly uploaded one (staged for review). */
  replaceProfileLicense: async (
    profileId: string,
    fileId: string,
    file: File,
  ): Promise<LicenseFile[]> => {
    const formData = new FormData();
    formData.append("business_license", file);
    const response = await client.post<ApiResponse<LicenseFile[]>>(
      URL_CONSTANTS.COMPANIES_API.PROFILE_LICENSE_REPLACE(profileId, fileId),
      formData,
    );
    return unwrap(response.data);
  },

  /** Remove a license file (staged for review on an approved company). */
  removeProfileLicense: async (
    profileId: string,
    fileId: string,
  ): Promise<LicenseFile[]> => {
    const response = await client.delete<ApiResponse<LicenseFile[]>>(
      URL_CONSTANTS.COMPANIES_API.PROFILE_LICENSE_FILE(profileId, fileId),
    );
    return unwrap(response.data);
  },

  /** The PoA delegation letter on file, with its review state. */
  getPoaDelegation: async (): Promise<LicenseFile[]> => {
    const response = await client.get<ApiResponse<LicenseFile[]>>(
      URL_CONSTANTS.COMPANIES_API.POA_DELEGATION,
    );
    return unwrap(response.data);
  },

  /**
   * Upload the PoA delegation letter, replacing any existing one. On an approved
   * company the upload is staged for backoffice review; during onboarding it
   * goes live immediately.
   */
  uploadPoaDelegation: async (file: File): Promise<LicenseFile[]> => {
    const formData = new FormData();
    formData.append("poa_delegation_letter", file);
    const response = await client.post<ApiResponse<LicenseFile[]>>(
      URL_CONSTANTS.COMPANIES_API.POA_DELEGATION,
      formData,
    );
    return unwrap(response.data);
  },

  /** Remove the PoA delegation letter (staged for review on an approved company). */
  removePoaDelegation: async (fileId: string): Promise<LicenseFile[]> => {
    const response = await client.delete<ApiResponse<LicenseFile[]>>(
      URL_CONSTANTS.COMPANIES_API.POA_DELEGATION_FILE(fileId),
    );
    return unwrap(response.data);
  },

  /** List business-license document(s) (with review state) for a company profile. */
  getProfileLicense: async (profileId: string): Promise<LicenseFile[]> => {
    const response = await client.get<ApiResponse<LicenseFile[]>>(
      URL_CONSTANTS.COMPANIES_API.PROFILE_LICENSE(profileId),
    );
    return unwrap(response.data);
  },

  /** The current company's open profile change request (pending/rejected), or null. */
  getChangeRequest: async (): Promise<ChangeRequestResponse | null> => {
    const response = await client.get<ApiResponse<ChangeRequestResponse | null>>(
      URL_CONSTANTS.COMPANIES_API.PROFILE_CHANGE_REQUEST,
    );
    return unwrap(response.data);
  },

  /** Resubmit a rejected operational role for approval (→ pending). */
  reapplyProfile: async (
    profileId: string,
  ): Promise<CompanyProfileResponse> => {
    const response = await client.post<ApiResponse<CompanyProfileResponse>>(
      URL_CONSTANTS.COMPANIES_API.PROFILE_REAPPLY(profileId),
    );
    return unwrap(response.data);
  },

  /** Fetch company registration data from eTrade by TIN. */
  fetchETradeInfo: async (payload: { tin: string }): Promise<any> => {
    const response = await client.post<ApiResponse<any>>(
      URL_CONSTANTS.COMPANIES_API.FETCH_ETRADE_INFO,
      payload,
    );
    return unwrap(response.data);
  },
};
