/**
 * 결재(APPROVAL) 서비스 — wace_plm ApprovalService 의 핵심 라이프사이클 포팅
 *
 *  - startApproval: 결재 상신 (approval + route + approval_target + inboxtask 일괄 생성)
 *  - listInbox:     결재함 목록 조회 (내가 처리해야 할 결재 / 작성한 결재 등 필터)
 *  - getDetail:     결재 상세 (approval / route / inboxtask 일괄)
 *  - approveTask:   특정 inboxtask 승인 → 다음 결재자에게 ready 전달, 마지막이면 route+approval+target 완료
 *  - rejectTask:    반려 → route/approval reject + target 상태 동기화
 *  - linkAmaranth:  아마란스 SSO URL 발급 후 amaranth_approval 매핑 저장
 *  - syncTargetStatus: target 테이블(ECR/CS 등)의 approval status 컬럼 동기화 (target_type 별 분기)
 */
import { query, queryOne, transaction } from "../database/db";
import { logger } from "../utils/logger";
import * as amaranth from "./amaranthApprovalClient";

export type ApprovalStatus = "inProcess" | "complete" | "reject" | "cancel";
export type InboxStatus = "ready" | "approve" | "reject" | "pending" | "hold" | "complete";

export interface StartApprovalParams {
  targetObjid: string | number;
  targetType: string;          // ECR_MNG / CUSTOMER_MNG / ...
  systemType?: string;
  title: string;
  description?: string;
  writer: string;
  approvers: Array<{ userId: string; type?: "approval" | "agree" | "refer" | "notice" }>;
}

export interface InboxListParams {
  connectUserId?: string;
  systemType?: string;
  approvalTitle?: string;
  fromDate?: string;
  toDate?: string;
  writerUserName?: string;
  approvalStatus?: string; // inProcess / complete / reject / cancel
  headerApprovalStatus?: "READY" | "ALL"; // READY=내 차례인 것만
  page?: number;
  pageSize?: number;
}

export class ApprovalService {
  /**
   * 결재 상신: 4개 테이블 INSERT 일괄
   */
  static async startApproval(p: StartApprovalParams) {
    return await transaction(async (client) => {
      const approvalObjid = Date.now();
      const routeObjid = approvalObjid + 1;

      // 1) APPROVAL
      await client.query(
        `INSERT INTO approval (objid, target_objid, target_type, approval_seq, system_type, status, regdate)
         VALUES ($1, $2::bigint, $3, COALESCE((SELECT MAX(approval_seq)+1 FROM approval), 1), $4, 'inProcess', NOW())`,
        [approvalObjid, String(p.targetObjid), p.targetType, p.systemType || ""],
      );

      // 2) ROUTE
      await client.query(
        `INSERT INTO route (objid, target_objid, approval_objid, route_seq, approval_title, approval_desc, system_type, writer, regdate, status)
         VALUES ($1, $2::bigint, $3, COALESCE((SELECT MAX(route_seq)+1 FROM route), 1), $4, $5, $6, $7, NOW(), 'inProcess')`,
        [routeObjid, String(p.targetObjid), approvalObjid, p.title, p.description || "", p.systemType || "", p.writer],
      );

      // 3) APPROVAL_TARGET
      const tgtObjid = approvalObjid + 2;
      await client.query(
        `INSERT INTO approval_target (objid, master_target_objid, target_objid, approval_objid, writer, regdate)
         VALUES ($1, $2::bigint, $3::bigint, $4, $5, NOW())`,
        [tgtObjid, String(p.targetObjid), String(p.targetObjid), approvalObjid, p.writer],
      );

      // 4) INBOXTASK — 첫 결재자만 ready, 나머지는 pending
      let seq = 1;
      let baseInbox = approvalObjid + 100;
      for (const ap of p.approvers) {
        const status: InboxStatus = seq === 1 ? "ready" : "pending";
        await client.query(
          `INSERT INTO inboxtask (objid, seq, approval_type, target_objid, approval_objid, route_objid, target_user_id, status, regdate)
           VALUES ($1, $2, $3, $4::bigint, $5, $6, $7, $8, NOW())`,
          [
            baseInbox + seq,
            seq,
            ap.type || "approval",
            String(p.targetObjid),
            approvalObjid,
            routeObjid,
            ap.userId,
            status,
          ],
        );
        seq++;
      }

      // 5) target 테이블의 approval status 컬럼 동기화 (best-effort)
      await this._syncTargetStatus(client, p.targetType, p.targetObjid, "inProcess");

      return { approvalObjid: String(approvalObjid), routeObjid: String(routeObjid) };
    });
  }

  /**
   * 결재함 목록 조회 (페이징)
   *  - connectUserId 가 있으면 그 사람의 ready inboxtask 가 있는 결재만
   *  - approvalStatus 가 있으면 status 필터
   */
  static async listInbox(p: InboxListParams) {
    const page = Math.max(1, Number(p.page) || 1);
    const pageSize = Math.max(1, Math.min(200, Number(p.pageSize) || 20));
    const offset = (page - 1) * pageSize;

    const where: string[] = [
      "A.objid = R.approval_objid",
      "I.approval_objid = A.objid",
      "I.route_objid = R.objid",
      "R.status = 'inProcess'",
      "LOWER(I.status) = 'ready'",
    ];
    const args: any[] = [];
    let i = 1;

    if (p.systemType)         { where.push(`A.system_type = $${i++}`); args.push(p.systemType); }
    if (p.connectUserId && p.connectUserId !== "plm_admin" && p.headerApprovalStatus !== "READY") {
      where.push(`I.target_user_id = $${i++}`);
      args.push(p.connectUserId);
    }
    if (p.approvalTitle)      { where.push(`UPPER(R.approval_title) LIKE UPPER('%' || $${i++} || '%')`); args.push(p.approvalTitle); }
    if (p.fromDate)           { where.push(`R.regdate >= $${i++}::timestamp`); args.push(p.fromDate); }
    if (p.toDate)             { where.push(`R.regdate <= $${i++}::timestamp`); args.push(p.toDate + " 23:59:59"); }
    if (p.writerUserName)     {
      where.push(`UPPER((SELECT user_name FROM user_info WHERE user_id = R.writer)) LIKE UPPER('%' || $${i++} || '%')`);
      args.push(p.writerUserName);
    }
    if (p.approvalStatus)     { where.push(`UPPER(A.status) = UPPER($${i++})`); args.push(p.approvalStatus); }

    if (p.headerApprovalStatus === "READY" && p.connectUserId) {
      where.push(`I.target_user_id = $${i++}`);
      args.push(p.connectUserId);
    }

    const whereSql = where.join(" AND ");

    const cnt = await queryOne<{ total: string }>(
      `SELECT COUNT(DISTINCT (A.objid, R.objid))::int AS total
         FROM approval A, route R, inboxtask I
        WHERE ${whereSql}`,
      args,
    );
    const total = Number(cnt?.total || 0);

    const rows = await query<any>(
      `
      SELECT DISTINCT
        A.objid::text       AS approval_objid,
        A.target_objid::text AS target_objid,
        A.target_type,
        A.approval_seq,
        REPLACE('APPROVAL-' || TO_CHAR(A.approval_seq::numeric, '0009'), ' ', '') AS approval_no,
        A.status            AS approval_status,
        TO_CHAR(A.regdate, 'YYYY-MM-DD HH24:MI:SS') AS approval_regdate,
        R.objid::text       AS route_objid,
        R.route_seq,
        R.approval_title,
        R.approval_desc,
        R.writer,
        (SELECT user_name FROM user_info WHERE user_id = R.writer) AS writer_user_name,
        (SELECT dept_name FROM user_info WHERE user_id = R.writer) AS writer_dept_name,
        TO_CHAR(R.regdate, 'YYYY-MM-DD HH24:MI:SS') AS route_regdate,
        R.status            AS route_status,
        I.target_user_id,
        AK.target_name
      FROM approval A, route R, inboxtask I
      LEFT JOIN approval_kind AK ON AK.target_type = (SELECT target_type FROM approval WHERE objid = I.approval_objid)
      WHERE ${whereSql}
      ORDER BY R.regdate DESC
      LIMIT $${i++} OFFSET $${i++}
      `,
      [...args, pageSize, offset],
    );

    return {
      list: rows,
      pagination: { page, pageSize, total, totalPages: Math.ceil(total / pageSize) },
    };
  }

  /** 결재 상세 — approval + route + 결재선(inboxtask) + 첨부 amaranth_approval */
  static async getDetail(approvalObjid: string | number) {
    const approval = await queryOne<any>(
      `SELECT A.*, AK.target_name,
              R.objid::text AS route_objid, R.approval_title, R.approval_desc, R.writer,
              (SELECT user_name FROM user_info WHERE user_id = R.writer) AS writer_user_name,
              TO_CHAR(R.regdate, 'YYYY-MM-DD HH24:MI:SS') AS route_regdate, R.status AS route_status,
              TO_CHAR(A.regdate, 'YYYY-MM-DD HH24:MI:SS') AS approval_regdate
         FROM approval A
         LEFT JOIN route R ON R.approval_objid = A.objid
         LEFT JOIN approval_kind AK ON AK.target_type = A.target_type
        WHERE A.objid = $1::bigint
        ORDER BY R.regdate DESC LIMIT 1`,
      [approvalObjid],
    );
    if (!approval) return null;

    const inboxtasks = await query<any>(
      `SELECT I.*,
              (SELECT user_name FROM user_info WHERE user_id = I.target_user_id) AS target_user_name,
              (SELECT dept_name FROM user_info WHERE user_id = I.target_user_id) AS target_dept_name,
              TO_CHAR(I.regdate, 'YYYY-MM-DD HH24:MI:SS') AS regdate_title,
              TO_CHAR(I.editdate, 'YYYY-MM-DD HH24:MI:SS') AS editdate_title
         FROM inboxtask I WHERE I.approval_objid = $1::bigint ORDER BY I.seq`,
      [approvalObjid],
    );

    const amaranthLink = await queryOne<any>(
      `SELECT * FROM amaranth_approval WHERE approval_objid = $1::bigint ORDER BY regdate DESC LIMIT 1`,
      [approvalObjid],
    );

    return { approval, inboxtasks, amaranthLink };
  }

  /** 승인 — 현재 task complete + 다음 task ready, 마지막이면 route/approval/target 모두 complete */
  static async approveTask(p: { inboxtaskObjid: string | number; userId: string; comment?: string }) {
    return await transaction(async (client) => {
      // 1) 현재 task → complete (approve)
      const cur = await client.query(
        `UPDATE inboxtask SET status='approve', comment=$1, editdate=NOW()
          WHERE objid=$2::bigint AND target_user_id=$3 AND LOWER(status)='ready'
          RETURNING objid, seq, approval_objid, route_objid, target_objid`,
        [p.comment || "", p.inboxtaskObjid, p.userId],
      );
      if (cur.rowCount === 0) throw new Error("처리 권한이 없거나 이미 처리된 결재입니다.");
      const { approval_objid, route_objid, target_objid } = cur.rows[0];

      // 2) 같은 approval/route 안의 다음 seq pending → ready 로 전환
      const next = await client.query(
        `UPDATE inboxtask SET status='ready', editdate=NOW()
          WHERE objid = (
            SELECT objid FROM inboxtask
             WHERE approval_objid=$1 AND route_objid=$2 AND LOWER(status)='pending'
             ORDER BY seq ASC LIMIT 1
          )
          RETURNING objid, target_user_id`,
        [approval_objid, route_objid],
      );

      // 3) 다음 task 가 없으면 route + approval + target 완료
      let completed = false;
      if (next.rowCount === 0) {
        await client.query(`UPDATE route    SET status='complete' WHERE objid=$1`, [route_objid]);
        await client.query(`UPDATE approval SET status='complete' WHERE objid=$1`, [approval_objid]);
        // target_type 조회 후 동기화
        const t = await client.query(`SELECT target_type FROM approval WHERE objid=$1`, [approval_objid]);
        if (t.rows[0]?.target_type) {
          await this._syncTargetStatus(client, t.rows[0].target_type, target_objid, "complete");
        }
        completed = true;
      }

      return { completed, nextApprover: next.rows[0]?.target_user_id || null };
    });
  }

  /** 반려 — 현재 task reject + route/approval reject + target 동기화 + 나머지 pending → hold */
  static async rejectTask(p: { inboxtaskObjid: string | number; userId: string; comment?: string }) {
    return await transaction(async (client) => {
      const cur = await client.query(
        `UPDATE inboxtask SET status='reject', comment=$1, editdate=NOW()
          WHERE objid=$2::bigint AND target_user_id=$3 AND LOWER(status)='ready'
          RETURNING approval_objid, route_objid, target_objid`,
        [p.comment || "", p.inboxtaskObjid, p.userId],
      );
      if (cur.rowCount === 0) throw new Error("처리 권한이 없거나 이미 처리된 결재입니다.");
      const { approval_objid, route_objid, target_objid } = cur.rows[0];

      await client.query(`UPDATE route    SET status='reject' WHERE objid=$1`, [route_objid]);
      await client.query(`UPDATE approval SET status='reject' WHERE objid=$1`, [approval_objid]);
      await client.query(
        `UPDATE inboxtask SET status='hold', editdate=NOW()
          WHERE approval_objid=$1 AND route_objid=$2 AND LOWER(status) IN ('pending','ready')`,
        [approval_objid, route_objid],
      );
      const t = await client.query(`SELECT target_type FROM approval WHERE objid=$1`, [approval_objid]);
      if (t.rows[0]?.target_type) {
        await this._syncTargetStatus(client, t.rows[0].target_type, target_objid, "reject");
      }
      return { rejected: true };
    });
  }

  /** 아마란스 SSO 연동 — approKey 기반 외부 결재 양식 띄우기 */
  static async linkAmaranth(args: {
    approvalObjid: string | number;
    targetObjid: string | number;
    targetType: string;
    empSeq: string;
    compSeq?: string;
    deptSeq?: string;
    outProcessCode?: string;
    formId?: string;
    subjectStr?: string;
    loginId?: string;
    writer: string;
  }) {
    const approKey = "UB_" + Date.now().toString(36).toUpperCase();
    const ssoRes = await amaranth.getSsoUrl({
      empSeq: args.empSeq,
      outProcessCode: args.outProcessCode,
      formId: args.formId,
      approKey,
      subjectStr: args.subjectStr,
      mod: "W",
      compSeq: args.compSeq,
      deptSeq: args.deptSeq,
      loginId: args.loginId,
    });

    const fullUrl = ssoRes?.resultData?.fullUrl || ssoRes?.fullUrl || null;

    const objid = Date.now();
    await query<any>(
      `INSERT INTO amaranth_approval
         (objid, approval_objid, target_objid, target_type, appro_key, out_process_code, form_id,
          status, emp_seq, comp_seq, dept_seq, writer, sso_url, regdate)
       VALUES ($1, $2, $3, $4, $5, $6, $7, 'pending', $8, $9, $10, $11, $12, NOW())`,
      [
        objid, args.approvalObjid, args.targetObjid, args.targetType, approKey,
        args.outProcessCode || null, args.formId || null,
        args.empSeq, args.compSeq || null, args.deptSeq || null, args.writer, fullUrl,
      ],
    );

    return { approKey, ssoUrl: fullUrl, raw: ssoRes };
  }

  /** target 테이블에 approval status 컬럼이 있을 경우 동기화 (best-effort) */
  private static async _syncTargetStatus(client: any, targetType: string, targetObjid: any, status: string) {
    const map: Record<string, { table: string; col: string }> = {
      ECR_MNG:      { table: "ecr_mng",      col: "status_cd" },
      CUSTOMER_MNG: { table: "customer_mng", col: "status" },
    };
    const m = map[targetType];
    if (!m) return;
    try {
      await client.query(
        `UPDATE ${m.table} SET ${m.col} = COALESCE(${m.col}, '') WHERE objid = $1::bigint`,
        [String(targetObjid)],
      );
      // 결재 status → 도메인 status 매핑은 도메인별 코드체계가 다르므로 일단 컬럼 존재 확인만 하고 끝.
      logger.debug(`[approval] target sync skipped — domain code mapping required for ${targetType}`);
    } catch (e: any) {
      logger.debug(`[approval] target sync ${targetType}: ${e?.message?.slice(0, 100)}`);
    }
  }

  /** 특정 target 의 최신 결재 한 건 조회 (현재 진행 중인 결재 보기용) */
  static async getLastApprovalByTarget(targetObjid: string | number, targetType?: string) {
    const where: string[] = ["target_objid = $1::bigint"];
    const args: any[] = [targetObjid];
    if (targetType) {
      where.push("target_type = $2");
      args.push(targetType);
    }
    return await queryOne<any>(
      `SELECT * FROM approval WHERE ${where.join(" AND ")} ORDER BY regdate DESC LIMIT 1`,
      args,
    );
  }
}
