import { Injectable, BadRequestException } from "@nestjs/common";
import { HttpService } from "@nestjs/axios";
import { Agent } from "https";
import { firstValueFrom } from "rxjs";
import {
  ETradeCompanyInfo,
  ETradeBusinessInfo,
  CompanyRegistrationData,
  normalizeRegion,
} from "@edr/types";

@Injectable()
export class ETradeService {
  private readonly baseUrl = "https://etrade.gov.et/api";
  private readonly referer = "https://etrade.gov.et/business-license-checker";

  /**
   * The eTrade server serves an incomplete TLS chain (it omits the intermediate
   * CA cert), so Node rejects the handshake with UNABLE_TO_GET_ISSUER_CERT.
   * Scope a relaxed agent to these outbound calls only — the rest of the app
   * keeps full certificate verification.
   */
  private readonly httpsAgent = new Agent({ rejectUnauthorized: false });

  constructor(private readonly httpService: HttpService) {}

  async getCompanyInfoByTin(tin: string): Promise<ETradeCompanyInfo> {
    const url = `${this.baseUrl}/Registration/GetRegistrationInfoByTin/${tin}/en`;
    try {
      const response = await firstValueFrom(
        this.httpService.get<ETradeCompanyInfo>(url, {
          headers: { Referer: this.referer },
          httpsAgent: this.httpsAgent,
        }),
      );
      return response.data;
    } catch (error: any) {
      throw new BadRequestException(
        `Failed to fetch company info from eTrade: ${error.message}`,
      );
    }
  }

  async getBusinessByLicenseNo(
    licenseNo: string,
    tin: string,
  ): Promise<ETradeBusinessInfo> {
    const url = `${this.baseUrl}/BusinessMain/GetBusinessByLicenseNo`;
    try {
      const response = await firstValueFrom(
        this.httpService.get<ETradeBusinessInfo>(url, {
          params: {
            LicenseNo: licenseNo,
            Tin: tin,
            Lang: "en",
          },
          headers: { Referer: this.referer },
          httpsAgent: this.httpsAgent,
        }),
      );
      return response.data;
    } catch (error: any) {
      throw new BadRequestException(
        `Failed to fetch business info from eTrade: ${error.message}`,
      );
    }
  }

  async resolveCompanyData(tin: string): Promise<{
    companyInfo: ETradeCompanyInfo;
    businessInfo: ETradeBusinessInfo | null;
  }> {
    const companyInfo = await this.getCompanyInfoByTin(tin);

    if (!companyInfo.Businesses || companyInfo.Businesses.length === 0) {
      return { companyInfo, businessInfo: null };
    }

    const latestBusiness = companyInfo.Businesses[0];
    try {
      const businessInfo = await this.getBusinessByLicenseNo(
        latestBusiness.LicenceNumber,
        tin,
      );
      return { companyInfo, businessInfo };
    } catch {
      return { companyInfo, businessInfo: null };
    }
  }

  /**
   * `companyInfo` carries the registered organization name (`BusinessName`);
   * `businessInfo` only carries the licence's `TradeName`. Pass both so the
   * company name resolves to the legal entity rather than the trade name — and
   * never to `ManagerNameEng`, which is the manager's personal name.
   */
  extractRegistrationData(
    businessInfo: ETradeBusinessInfo,
    companyInfo?: ETradeCompanyInfo,
  ): CompanyRegistrationData {
    const primaryManager = businessInfo.AssociateShortInfos?.[0];

    return {
      companyName:
        companyInfo?.BusinessName?.trim() || businessInfo.TradeName?.trim() || "",
      licenceNumber: businessInfo.LicenceNumber,
      statusDescription: businessInfo.StatusDescription,
      dateRegistered: businessInfo.DateRegistered,
      renewedFrom: businessInfo.RenewedFrom,
      renewalDate: businessInfo.RenewalDate,
      renewedTo: businessInfo.RenewedTo,
      // eTrade returns uncoded uppercase text and sometimes a zone name in the
      // Region slot. Map it onto the canonical list; an unresolved value yields
      // "" so the form asks the user to pick rather than failing validation on
      // save with a value they never typed.
      region: normalizeRegion(businessInfo.AddressInfo?.Region) ?? "",
      zone: businessInfo.AddressInfo?.Zone || "",
      woreda: businessInfo.AddressInfo?.Woreda || "",
      kebele: businessInfo.AddressInfo?.Kebele || "",
      houseNo: businessInfo.AddressInfo?.HouseNo || "",
      mobilePhone: businessInfo.AddressInfo?.MobilePhone || "",
      regularPhone: businessInfo.AddressInfo?.RegularPhone || "",
      managerName: primaryManager?.ManagerNameEng || "",
      managerPhone: primaryManager?.RegularPhone || "",
    };
  }
}
