/**
 * RPS / Wehago ERP REST API 클라이언트
 *
 * 참고 원본: /Users/chpark/wace_plm/src/com/pms/api (Java)
 *  - EmployeeApiClient (api16S05)
 *  - DepartmentApiClient (api16S10)
 *  - CustomerApiClient (api16S11)
 *  - WarehouseApiClient (api20A00S00801)
 *  - AccountCodeApiClient (api11A02)
 *
 * Wehago 인증 프로토콜:
 *  - callerName, Authorization(Bearer), transaction-id(32 hex), timestamp(unix sec),
 *    groupSeq, wehago-sign(HMAC-SHA256(accessToken+transactionId+timestamp+urlPath, HASH_KEY) → Base64)
 */
import crypto from "crypto";
import https from "https";
import http from "http";
import { URL as NodeURL } from "url";
import { logger } from "../utils/logger";

const DEFAULT_BASE_URL = process.env.ERP_BASE_URL || "https://erp.rps-korea.com";
const CALLER_NAME = process.env.ERP_CALLER_NAME || "API_gcmsAmaranth40578";
const ACCESS_TOKEN = process.env.ERP_ACCESS_TOKEN || "MN5KzKBWRAa92BPxDlRLl3GcsxeZXc";
const HASH_KEY = process.env.ERP_HASH_KEY || "22519103205540290721741689643674301018832465";
const GROUP_SEQ = process.env.ERP_GROUP_SEQ || "gcmsAmaranth40578";

// SSL 인증서 검증 우회 (Java 클라이언트와 동일 — 개발/내부망 호환)
const insecureAgent = new https.Agent({ rejectUnauthorized: false });

/**
 * Node.js native http(s) 기반 POST 호출.
 *  - 자체 서명 SSL 인증서를 받아들여야 하므로 https.Agent({ rejectUnauthorized: false }) 사용.
 *  - 30xx 리다이렉트 시 동일 헤더로 한 번 추적.
 */
function postJson(
  fullUrl: string,
  headers: Record<string, string>,
  body: string
): Promise<{ status: number; body: string }> {
  return new Promise((resolve, reject) => {
    const u = new NodeURL(fullUrl);
    const isHttps = u.protocol === "https:";
    const reqOptions: https.RequestOptions = {
      method: "POST",
      hostname: u.hostname,
      port: u.port || (isHttps ? 443 : 80),
      path: u.pathname + (u.search || ""),
      headers: { ...headers, "Content-Length": Buffer.byteLength(body, "utf8").toString() },
      timeout: 30000,
      ...(isHttps ? { agent: insecureAgent } : {}),
    };

    const req = (isHttps ? https : http).request(reqOptions, (res) => {
      const status = res.statusCode || 0;
      // 리다이렉트
      if ([301, 302, 303, 307, 308].includes(status) && res.headers.location) {
        res.resume();
        const next = res.headers.location.startsWith("http")
          ? res.headers.location
          : new NodeURL(res.headers.location, fullUrl).toString();
        postJson(next, headers, body).then(resolve, reject);
        return;
      }
      const chunks: Buffer[] = [];
      res.on("data", (c) => chunks.push(c));
      res.on("end", () => {
        resolve({ status, body: Buffer.concat(chunks).toString("utf8") });
      });
      res.on("error", reject);
    });

    req.on("timeout", () => {
      req.destroy(new Error("ERP API timeout (30s)"));
    });
    req.on("error", reject);
    req.write(body, "utf8");
    req.end();
  });
}

interface CallOptions {
  baseUrl?: string;
  apiPath: string; // ex) "/apiproxy/api16S05"
  body: Record<string, any>;
}

function generateTransactionId(): string {
  return crypto.randomBytes(16).toString("hex");
}

function generateWehagoSign(transactionId: string, timestamp: string, urlPath: string): string {
  const value = ACCESS_TOKEN + transactionId + timestamp + urlPath;
  return crypto.createHmac("sha256", HASH_KEY).update(value, "utf8").digest("base64");
}

async function callErpApi<T = any>(options: CallOptions): Promise<T> {
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
  const urlPath = options.apiPath;
  const fullUrl = baseUrl + urlPath;

  const transactionId = generateTransactionId();
  const timestamp = String(Math.floor(Date.now() / 1000));
  const wehagoSign = generateWehagoSign(transactionId, timestamp, urlPath);

  const headers: Record<string, string> = {
    "Content-Type": "application/json; charset=UTF-8",
    Accept: "application/json",
    callerName: CALLER_NAME,
    Authorization: `Bearer ${ACCESS_TOKEN}`,
    "transaction-id": transactionId,
    timestamp,
    groupSeq: GROUP_SEQ,
    "wehago-sign": wehagoSign,
  };

  const payload = JSON.stringify(options.body);
  const { status, body: text } = await postJson(fullUrl, headers, payload);

  if (status < 200 || status >= 300) {
    logger.error("[erpApiClient] 호출 실패", {
      url: fullUrl,
      status,
      body: text?.slice(0, 500),
    });
    throw new Error(`ERP API 호출 실패: HTTP ${status} - ${text}`);
  }

  try {
    return JSON.parse(text) as T;
  } catch (e) {
    logger.error("[erpApiClient] JSON 파싱 실패", { text: text?.slice(0, 500) });
    throw new Error("ERP API 응답이 JSON 형식이 아닙니다.");
  }
}

/**
 * 사원 정보 조회 (api16S05)
 *  - extraColumns로 emalAdd / outemalAdd / emgcTel / tel / joinDt 추가 요청
 */
export async function fetchEmployees(coCd: string, baseUrl?: string): Promise<any> {
  if (!coCd?.trim()) throw new Error("회사코드(coCd)는 필수입니다.");
  return callErpApi({
    baseUrl,
    apiPath: "/apiproxy/api16S05",
    body: {
      header: { groupSeq: GROUP_SEQ, empSeq: "", tId: "", pId: "" },
      body: {
        coCd,
        extraColumns: ["emalAdd", "outemalAdd", "emgcTel", "tel", "joinDt"],
      },
    },
  });
}

/**
 * 부서 정보 조회 (api16S10)
 */
export async function fetchDepartments(
  coCd: string,
  searchText?: string,
  baseUrl?: string
): Promise<any> {
  if (!coCd?.trim()) throw new Error("회사코드(coCd)는 필수입니다.");
  const body: Record<string, any> = { coCd };
  if (searchText && searchText.trim()) body.searchText = searchText.trim();
  return callErpApi({ baseUrl, apiPath: "/apiproxy/api16S10", body });
}

/**
 * 거래처 정보 조회 (api16S11)
 */
export async function fetchCustomers(coCd: string, baseUrl?: string): Promise<any> {
  if (!coCd?.trim()) throw new Error("회사코드(coCd)는 필수입니다.");
  return callErpApi({ baseUrl, apiPath: "/apiproxy/api16S11", body: { coCd } });
}

/**
 * 창고 정보 조회 (api20A00S00801)
 *  baselocFg: "0" 전체, "1" 기준위치만 (선택)
 */
export async function fetchWarehouses(
  coCd: string,
  baselocFg?: string,
  baseUrl?: string
): Promise<any> {
  if (!coCd?.trim()) throw new Error("회사코드(coCd)는 필수입니다.");
  const body: Record<string, any> = { coCd };
  if (baselocFg && baselocFg.trim()) body.baselocFg = baselocFg;
  return callErpApi({ baseUrl, apiPath: "/apiproxy/api20A00S00801", body });
}

/**
 * 계정과목 정보 조회 (api11A02)
 */
export async function fetchAccountCodes(coCd: string, baseUrl?: string): Promise<any> {
  if (!coCd?.trim()) throw new Error("회사코드(coCd)는 필수입니다.");
  return callErpApi({ baseUrl, apiPath: "/apiproxy/api11A02", body: { coCd } });
}

/**
 * 응답에서 데이터 배열을 안전하게 추출.
 *  Wehago 응답은 보통 { result: { data: [...] } } | { data: [...] } | { body: { data: [...] } } | [...]
 */
export function extractRecords(response: any): any[] {
  if (!response) return [];
  if (Array.isArray(response)) return response;
  const candidates = [
    response?.data,
    response?.result?.data,
    response?.body?.data,
    response?.body?.result?.data,
    response?.resultData,
    response?.list,
    response?.rows,
  ];
  for (const c of candidates) {
    if (Array.isArray(c)) return c;
  }
  // 객체 내 첫 번째 배열 속성 fallback
  if (typeof response === "object") {
    for (const v of Object.values(response)) {
      if (Array.isArray(v)) return v;
    }
  }
  return [];
}
