export type EtradeLanguage = "en" | "am";

export interface EtradeBusinessSubGroup {
  Code?: number;
  Description?: string;
}

export interface EtradeBusiness {
  MainGuid?: string;
  OwnerTIN?: string;
  DateRegistered?: string;
  TradeNameAmh?: string;
  TradesName?: string;
  LicenceNumber?: string;
  LicenseNumber?: string;
  RenewalDate?: string;
  RenewedFrom?: string;
  RenewedTo?: string;
  SubGroups?: EtradeBusinessSubGroup[];
}

export interface EtradeBusinessLicenseOption {
  mainGuid: string;
  licenseNumber: string;
  tradeName: string;
  activities: string[];
  renewedTo?: string;
}

export interface EtradeRegistrationInfo {
  Tin?: string;
  BusinessName?: string;
  BusinessNameAmh?: string;
  RegNo?: string;
  Businesses?: EtradeBusiness[];
  tin?: string;
  businessName?: string;
  businessNameAmh?: string;
  regNo?: string;
  businesses?: EtradeBusiness[];
  [key: string]: unknown;
}

export class TinRegistrationNotFoundError extends Error {
  readonly code = "TIN_NOT_FOUND" as const;

  constructor() {
    super("TIN_NOT_FOUND");
    this.name = "TinRegistrationNotFoundError";
  }
}

const ETRADE_API_BASE =
  import.meta.env.VITE_ETRADE_API_BASE?.trim() || "/api/etrade";

export function resolveEtradeLanguage(language: string): EtradeLanguage {
  return language.toLowerCase().startsWith("am") ? "am" : "en";
}

function hasRegistrationData(data: unknown): data is EtradeRegistrationInfo {
  if (data == null) return false;
  if (typeof data !== "object") return false;
  if (Array.isArray(data)) return data.length > 0;

  const record = data as Record<string, unknown>;
  const keys = Object.keys(record);
  if (keys.length === 0) return false;

  const tin = String(record.Tin ?? record.tin ?? "").trim();
  const businessName = String(
    record.BusinessName ?? record.businessName ?? "",
  ).trim();

  return Boolean(tin || businessName);
}

function normalizeEtradeBusinesses(
  data: EtradeRegistrationInfo,
): EtradeBusiness[] {
  const businesses = data.Businesses ?? data.businesses;
  return Array.isArray(businesses) ? businesses : [];
}

export function mapEtradeBusinessLicenses(
  data: EtradeRegistrationInfo,
  language: EtradeLanguage,
): EtradeBusinessLicenseOption[] {
  const companyName = String(
    data.BusinessName ?? data.businessName ?? "",
  ).trim();
  const companyNameAmh = String(
    data.BusinessNameAmh ?? data.businessNameAmh ?? "",
  ).trim();

  return normalizeEtradeBusinesses(data).flatMap((business) => {
    const licenseNumber = String(
      business.LicenceNumber ?? business.LicenseNumber ?? "",
    ).trim();
    if (!licenseNumber) return [];

    const tradeName = String(business.TradesName ?? "").trim();
    const tradeNameAmh = String(business.TradeNameAmh ?? "").trim();
    const activities = (business.SubGroups ?? [])
      .map((group) => String(group.Description ?? "").trim())
      .filter(Boolean);

    const displayTradeName =
      language === "am"
        ? tradeNameAmh || tradeName || companyNameAmh || companyName
        : tradeName || tradeNameAmh || companyName || companyNameAmh;

    const option: EtradeBusinessLicenseOption = {
      mainGuid: String(business.MainGuid ?? licenseNumber).trim(),
      licenseNumber,
      tradeName: displayTradeName || licenseNumber,
      activities,
    };

    const renewedTo = String(business.RenewedTo ?? "").trim();
    if (renewedTo) {
      option.renewedTo = renewedTo;
    }

    return [option];
  });
}

export function mapEtradeRegistration(
  data: EtradeRegistrationInfo,
  fallbackTin: string,
) {
  const tin = String(data.Tin ?? data.tin ?? fallbackTin).trim();
  const organizationName = String(
    data.BusinessName ?? data.businessName ?? "",
  ).trim();
  const regNo = String(data.RegNo ?? data.regNo ?? "").trim();

  return {
    organizationName: organizationName || tin,
    tin,
    ...(regNo ? { regNo } : {}),
  };
}

export interface EtradeBusinessByLicense {
  MainGuid?: string;
  OwnerTIN?: string;
  TradeName?: string;
  LicenceNumber?: string;
  LicenseNumber?: string;
  AssociateShortInfos?: Array<{
    Position?: string;
    ManagerName?: string;
    ManagerNameEng?: string;
    MobilePhone?: string;
    RegularPhone?: string;
  }>;
  AddressInfo?: {
    Region?: string;
    Zone?: string;
    Woreda?: string;
    Kebele?: string;
    HouseNo?: string;
    MobilePhone?: string;
    RegularPhone?: string;
  };
  [key: string]: unknown;
}

/**
 * Normalize Ethiopian phone numbers for IAM auth.
 * Accepts 09/07 mobiles and common 0X landline forms; returns +251...
 */
export function normalizeEtradePhoneNumber(
  raw: string | null | undefined,
): string | null {
  if (!raw?.trim()) return null;

  let digits = raw.trim().replace(/[^\d+]/g, "");
  if (digits.startsWith("+")) {
    digits = digits.slice(1);
  }

  // Already international without +
  if (digits.startsWith("251") && digits.length >= 12) {
    return `+${digits}`;
  }

  // Local with leading 0 (mobile or landline): 09xxxxxxxx / 07xxxxxxxx / 0Xxxxxxxx
  if (digits.startsWith("0") && digits.length >= 9) {
    return `+251${digits.slice(1)}`;
  }

  // Mobile without leading 0: 9xxxxxxxx / 7xxxxxxxx
  if (/^[97]\d{8}$/.test(digits)) {
    return `+251${digits}`;
  }

  // Bare landline-ish digits (e.g. 52543000) — prefix country code only if 8–9 digits
  if (/^\d{8,9}$/.test(digits)) {
    return `+251${digits}`;
  }

  return null;
}

function pickRawPhoneFromBusiness(data: EtradeBusinessByLicense): string | null {
  const associates = Array.isArray(data.AssociateShortInfos)
    ? data.AssociateShortInfos
    : [];

  // Prefer RegularPhone — eTrade MobilePhone is often a landline/invalid value
  // (e.g. "52543000", "222400000") while RegularPhone holds the mobile (09...).
  for (const associate of associates) {
    const regular = String(associate.RegularPhone ?? "").trim();
    if (regular) return regular;
  }

  const addressRegular = String(data.AddressInfo?.RegularPhone ?? "").trim();
  if (addressRegular) return addressRegular;

  for (const associate of associates) {
    const mobile = String(associate.MobilePhone ?? "").trim();
    if (mobile) return mobile;
  }

  const addressMobile = String(data.AddressInfo?.MobilePhone ?? "").trim();
  if (addressMobile) return addressMobile;

  return null;
}

export function extractEtradePhoneNumber(
  data: EtradeBusinessByLicense,
): string | null {
  return normalizeEtradePhoneNumber(pickRawPhoneFromBusiness(data));
}

export async function fetchBusinessByLicenseNo(
  licenseNo: string,
  tin: string,
  language: EtradeLanguage,
): Promise<EtradeBusinessByLicense> {
  const params = new URLSearchParams({
    LicenseNo: licenseNo.trim(),
    Tin: tin.trim(),
    Lang: language,
  });
  const url = `${ETRADE_API_BASE}/BusinessMain/GetBusinessByLicenseNo?${params.toString()}`;

  let response: Response;
  try {
    response = await fetch(url, {
      method: "GET",
      headers: {
        Accept: "application/json",
      },
    });
  } catch (error) {
    console.error("[eTrade:License] Network request failed", error);
    throw error;
  }

  const responseText = (await response.text()).trim();

  if (response.status === 417) {
    console.error(
      "[eTrade:License] Referer rejected by eTrade. Ensure /api/etrade proxy is configured.",
      responseText,
    );
    throw new Error("ETRADE_REFERER_REJECTED");
  }

  if (!response.ok && response.status !== 404) {
    throw new Error(`ETRADE_HTTP_${response.status}`);
  }

  if (!responseText) {
    throw new Error("ETRADE_BUSINESS_NOT_FOUND");
  }

  let data: unknown;
  try {
    data = JSON.parse(responseText);
  } catch (parseError) {
    console.error(
      "[eTrade:License] Invalid JSON response",
      parseError,
      responseText,
    );
    throw new Error("INVALID_ETRADE_RESPONSE");
  }

  if (data == null || typeof data !== "object") {
    throw new Error("ETRADE_BUSINESS_NOT_FOUND");
  }

  return data as EtradeBusinessByLicense;
}

/**
 * Looks up business details by license + TIN and returns a normalized phone
 * suitable for IAM signup (`+251...`).
 */
export async function resolveEtradePhoneForSignup(
  licenseNo: string,
  tin: string,
  language: EtradeLanguage = "en",
): Promise<string | null> {
  const business = await fetchBusinessByLicenseNo(licenseNo, tin, language);
  return extractEtradePhoneNumber(business);
}

export async function fetchRegistrationByTin(
  tin: string,
  language: EtradeLanguage,
): Promise<EtradeRegistrationInfo> {
  const normalizedTin = tin.trim();
  const url = `${ETRADE_API_BASE}/Registration/GetRegistrationInfoByTin/${encodeURIComponent(normalizedTin)}/${language}`;

  let response: Response;
  try {
    response = await fetch(url, {
      method: "GET",
      headers: {
        Accept: "application/json",
      },
    });
  } catch (error) {
    console.error("[eTrade:TIN] Network request failed", error);
    throw error;
  }

  const responseText = (await response.text()).trim();

  if (response.status === 417) {
    console.error(
      "[eTrade:TIN] Referer rejected by eTrade. Ensure /api/etrade proxy is configured.",
      responseText,
    );
    throw new Error("ETRADE_REFERER_REJECTED");
  }

  if (!response.ok && response.status !== 404) {
    throw new Error(`ETRADE_HTTP_${response.status}`);
  }

  if (!responseText) {
    throw new TinRegistrationNotFoundError();
  }

  let data: unknown;
  try {
    data = JSON.parse(responseText);
  } catch (parseError) {
    console.error("[eTrade:TIN] Invalid JSON response", parseError, responseText);
    throw new Error("INVALID_ETRADE_RESPONSE");
  }

  if (data == null || !hasRegistrationData(data)) {
    throw new TinRegistrationNotFoundError();
  }

  return data;
}
