import * as crypto from "crypto";
import axios, { AxiosRequestConfig } from "axios";
import FormData from "form-data";

const SDK_VERSION = "lazop-sdk-js-20250422";
const AUTH_URL = "https://auth.lazada.com/oauth/authorize";

const APIGateway: Record<string, string> = {
  SG: "https://api.lazada.sg/rest",
  MY: "https://api.lazada.com.my/rest",
  VN: "https://api.lazada.vn/rest",
  TH: "https://api.lazada.co.th/rest",
  PH: "https://api.lazada.com.ph/rest",
  ID: "https://api.lazada.co.id/rest",
};

interface LazadaClientOptions {
  apiKey: string;
  apiSecret: string;
  region: keyof typeof APIGateway;
  callbackUrl: string;
  debug?: boolean;
}

class LazadaClient {
  private apiKey: string;
  private apiSecret: string;
  private region: keyof typeof APIGateway;
  private callbackUrl: string;
  private sysParams: Record<string, string>;
  private apiParams: Record<string, string | number>;
  private fileParams: Record<string, Buffer>;
  private debug: boolean;

  constructor({
    apiKey,
    apiSecret,
    region,
    callbackUrl,
    debug = false,
  }: LazadaClientOptions) {
    if (!APIGateway[region]) throw new Error(`Unsupported region: ${region}`);
    this.apiKey = apiKey;
    this.apiSecret = apiSecret;
    this.region = region;
    this.callbackUrl = callbackUrl;
    this.debug = debug;
    this.sysParams = {
      app_key: this.apiKey,
      sign_method: "sha256",
    };
    this.apiParams = {};
    this.fileParams = {};
  }

  private generateTimestamp(): number {
    return Date.now();
  }

  private getSysParams(): Record<string, number> {
    return {
      ...this.sysParams,
      timestamp: this.generateTimestamp(),
    };
  }

  setAccessToken(token: string): this {
    this.sysParams.access_token = token;
    return this;
  }

  changeRegion(region: keyof typeof APIGateway): this {
    if (!APIGateway[region]) throw new Error(`Unsupported region: ${region}`);
    this.region = region;
    return this;
  }

  addAPIParam(key: string, val: string | number): this {
    this.apiParams[key] = val;
    return this;
  }

  addFileParam(key: string, val: Buffer): this {
    this.fileParams[key] = val;
    return this;
  }

  setCallbackUrl(url: string): void {
    this.callbackUrl = url;
  }

  makeAuthURL(): string {
    const params = new URLSearchParams({
      response_type: "code",
      force_auth: "true",
      country: this.region.toLowerCase(),
      redirect_uri: this.callbackUrl,
      client_id: this.apiKey,
    });
    return `${AUTH_URL}?${params.toString()}`;
  }

  async requestToken(code: string): Promise<any> {
    const timestamp = this.generateTimestamp();
    const sysParams = this.getSysParams();

    const params = {
      ...sysParams,
      code,
      app_key: this.apiKey,
      sign_method: "sha256",
      timestamp,
    };

    const sign = this.sign("/auth/token/create", params);
    const allParams = { ...params, sign };
    const urlParams = new URLSearchParams(
      Object.entries(allParams).map(([k, v]) => [k, String(v)])
    );
    const fullUrl = `https://auth.lazada.com/rest/auth/token/create?${urlParams.toString()}`;

    if (this.debug) {
      console.log("🛠️ Lazada Token Request Debug:");
      console.log("URL:", fullUrl);
      console.log("Params:", allParams);
      console.log("Sign:", sign);
    }

    try {
      const response = await axios.get(fullUrl);
      return response.data;
    } catch (err: any) {
      if (this.debug) {
        console.error(
          "❌ Lazada API Error:",
          err.response?.data || err.message
        );
      }
      throw err;
    }
  }

  private sign(
    path: string,
    parameters: Record<string, string | number>
  ): string {
    const sortedKeys = Object.keys(parameters).sort();
    let paramStr = path;

    if (this.debug) {
      console.log("🔐 SIGNING DEBUG:");
      console.log("Path:", path);
      console.log("Sorted Params:");
      sortedKeys.forEach((key) => {
        console.log(`  ${key}: ${parameters[key]}`);
      });
    }

    sortedKeys.forEach((key) => {
      paramStr += key + String(parameters[key]);
    });

    const signature = crypto
      .createHmac("sha256", this.apiSecret)
      .update(paramStr)
      .digest("hex")
      .toUpperCase();

    if (this.debug) {
      console.log("🔏 Signature Base String:", paramStr);
      console.log("🔑 Signature Result:", signature);
    }

    return signature;
  }

  private getServerURL(): string {
    const url = APIGateway[this.region];
    if (!url) throw new Error(`Region "${this.region}" is not supported.`);
    return url;
  }

  async execute(
    apiPath: string,
    apiMethod: "GET" | "POST" = "GET",
    bodyParams: Record<string, string | number> = {}
  ): Promise<any> {
    const sysParams = this.getSysParams();
    const sign = this.sign(apiPath, { ...sysParams, ...this.apiParams });
    const allParams = { ...sysParams, ...this.apiParams, sign };
    const urlParams = new URLSearchParams(
      Object.entries(allParams).map(([k, v]) => [k, String(v)])
    );
    const fullUrl = `${this.getServerURL()}${apiPath}?${urlParams.toString()}`;

    if (this.debug) {
      console.log("🛠️ Lazada Request Debug:");
      console.log("URL:", fullUrl);
      console.log("Method:", apiMethod);
      console.log("Sys Params:", sysParams);
      console.log("API Params:", this.apiParams);
      console.log("Sign:", sign);
    }

    const config: AxiosRequestConfig = {
      method: apiMethod,
      url: fullUrl,
      headers: {},
    };

    if (apiMethod === "POST") {
      const form = new FormData();

      Object.entries(this.apiParams).forEach(([key, val]) =>
        form.append(key, String(val))
      );
      Object.entries(bodyParams).forEach(([key, val]) =>
        form.append(key, String(val))
      );
      Object.entries(this.fileParams).forEach(([filename, buffer]) =>
        form.append("image", buffer, filename)
      );

      config.data = form;
      config.headers = form.getHeaders();
    }

    try {
      const response = await axios(config);
      return response.data;
    } catch (err: any) {
      if (this.debug) {
        console.error(
          "❌ Lazada API Error:",
          err.response?.data || err.message
        );
      }
      throw err;
    } finally {
      this.apiParams = {};
      this.fileParams = {};
    }
  }
}

export default LazadaClient;
