import { query } from "../database/db";
import { logger } from "../utils/logger";

/**
 * 권한 그룹 인터페이스
 */
export interface RoleGroup {
  objid: number;
  authName: string;
  authCode: string;
  companyCode: string;
  status: string;
  writer: string;
  regdate: Date;
  memberCount?: number;
  menuCount?: number;
  memberNames?: string;
}

/**
 * 권한 그룹 멤버 인터페이스
 */
export interface RoleMember {
  objid: number;
  masterObjid: number;
  userId: string;
  userName?: string;
  deptName?: string;
  positionName?: string;
  writer: string;
  regdate: Date;
}

/**
 * 메뉴 권한 인터페이스
 */
export interface MenuPermission {
  objid: number;
  menuObjid: number;
  authObjid: number;
  menuName?: string;
  createYn: string;
  readYn: string;
  updateYn: string;
  deleteYn: string;
  writer: string;
  regdate: Date;
}

/**
 * 권한 그룹 서비스
 */
export class RoleService {
  /**
   * 회사별 권한 그룹 목록 조회
   * @param companyCode - 회사 코드 (undefined 시 전체 조회)
   * @param search - 검색어
   */
  static async getRoleGroups(
    companyCode?: string,
    search?: string
  ): Promise<RoleGroup[]> {
    try {
      let sql = `
        SELECT
          objid,
          auth_name AS "authName",
          auth_code AS "authCode",
          company_code AS "companyCode",
          status,
          writer,
          regdate,
          (SELECT COUNT(*) FROM authority_sub_user asu WHERE asu.master_objid = am.objid) AS "memberCount",
          (SELECT COUNT(*) FROM rel_menu_auth rma WHERE rma.auth_objid = am.objid) AS "menuCount",
          (SELECT STRING_AGG(ui.user_name, ', ' ORDER BY ui.user_name) 
           FROM authority_sub_user asu 
           JOIN user_info ui ON asu.user_id = ui.user_id 
           WHERE asu.master_objid = am.objid) AS "memberNames"
        FROM authority_master am
        WHERE 1=1
      `;

      const params: any[] = [];
      let paramIndex = 1;

      // 회사 코드 필터 (companyCode가 undefined면 전체 조회)
      if (companyCode) {
        sql += ` AND company_code = $${paramIndex}`;
        params.push(companyCode);
        paramIndex++;
      }

      // 검색어 필터
      if (search && search.trim()) {
        sql += ` AND (auth_name ILIKE $${paramIndex} OR auth_code ILIKE $${paramIndex})`;
        params.push(`%${search.trim()}%`);
        paramIndex++;
      }

      sql += ` ORDER BY regdate DESC`;

      logger.info("권한 그룹 조회 SQL", { sql, params });
      const result = await query<RoleGroup>(sql, params);
      logger.info("권한 그룹 조회 결과", { count: result.length });
      return result;
    } catch (error) {
      logger.error("권한 그룹 목록 조회 실패", { error, companyCode, search });
      throw error;
    }
  }

  /**
   * 권한 그룹 상세 조회
   */
  static async getRoleGroupById(objid: number): Promise<RoleGroup | null> {
    try {
      const sql = `
        SELECT
          objid,
          auth_name AS "authName",
          auth_code AS "authCode",
          company_code AS "companyCode",
          status,
          writer,
          regdate
        FROM authority_master
        WHERE objid = $1
      `;

      const result = await query<RoleGroup>(sql, [objid]);
      return result.length > 0 ? result[0] : null;
    } catch (error) {
      logger.error("권한 그룹 상세 조회 실패", { error, objid });
      throw error;
    }
  }

  /**
   * 권한 그룹 생성
   */
  static async createRoleGroup(data: {
    authName: string;
    authCode: string;
    companyCode: string;
    writer: string;
  }): Promise<RoleGroup> {
    try {
      const sql = `
        INSERT INTO authority_master (objid, auth_name, auth_code, company_code, status, writer, regdate)
        VALUES (nextval('seq_authority_master'), $1, $2, $3, 'active', $4, NOW())
        RETURNING objid, auth_name AS "authName", auth_code AS "authCode", 
                  company_code AS "companyCode", status, writer, regdate
      `;

      const result = await query<RoleGroup>(sql, [
        data.authName,
        data.authCode,
        data.companyCode,
        data.writer,
      ]);

      logger.info("권한 그룹 생성 성공", {
        objid: result[0].objid,
        authName: data.authName,
      });
      return result[0];
    } catch (error) {
      logger.error("권한 그룹 생성 실패", { error, data });
      throw error;
    }
  }

  /**
   * 권한 그룹 수정
   */
  static async updateRoleGroup(
    objid: number,
    data: {
      authName?: string;
      authCode?: string;
      status?: string;
    }
  ): Promise<RoleGroup> {
    try {
      const updates: string[] = [];
      const params: any[] = [];
      let paramIndex = 1;

      if (data.authName !== undefined) {
        updates.push(`auth_name = $${paramIndex}`);
        params.push(data.authName);
        paramIndex++;
      }

      if (data.authCode !== undefined) {
        updates.push(`auth_code = $${paramIndex}`);
        params.push(data.authCode);
        paramIndex++;
      }

      if (data.status !== undefined) {
        updates.push(`status = $${paramIndex}`);
        params.push(data.status);
        paramIndex++;
      }

      if (updates.length === 0) {
        throw new Error("수정할 데이터가 없습니다");
      }

      params.push(objid);

      const sql = `
        UPDATE authority_master
        SET ${updates.join(", ")}
        WHERE objid = $${paramIndex}
        RETURNING objid, auth_name AS "authName", auth_code AS "authCode", 
                  company_code AS "companyCode", status, writer, regdate
      `;

      const result = await query<RoleGroup>(sql, params);

      if (result.length === 0) {
        throw new Error("권한 그룹을 찾을 수 없습니다");
      }

      logger.info("권한 그룹 수정 성공", { objid, updates });
      return result[0];
    } catch (error) {
      logger.error("권한 그룹 수정 실패", { error, objid, data });
      throw error;
    }
  }

  /**
   * 권한 그룹 삭제
   */
  static async deleteRoleGroup(objid: number): Promise<void> {
    try {
      // CASCADE로 연결된 데이터도 함께 삭제됨 (authority_sub_user, rel_menu_auth)
      await query("DELETE FROM authority_master WHERE objid = $1", [objid]);
      logger.info("권한 그룹 삭제 성공", { objid });
    } catch (error) {
      logger.error("권한 그룹 삭제 실패", { error, objid });
      throw error;
    }
  }

  /**
   * 권한 그룹 멤버 목록 조회
   */
  static async getRoleMembers(masterObjid: number): Promise<RoleMember[]> {
    try {
      const sql = `
        SELECT
          asu.objid,
          asu.master_objid AS "masterObjid",
          asu.user_id AS "userId",
          ui.user_name AS "userName",
          ui.dept_name AS "deptName",
          ui.position_name AS "positionName",
          asu.writer,
          asu.regdate
        FROM authority_sub_user asu
        JOIN user_info ui ON asu.user_id = ui.user_id
        WHERE asu.master_objid = $1
        ORDER BY ui.user_name
      `;

      const result = await query<RoleMember>(sql, [masterObjid]);
      return result;
    } catch (error) {
      logger.error("권한 그룹 멤버 조회 실패", { error, masterObjid });
      throw error;
    }
  }

  /**
   * 권한 그룹 멤버 추가 (여러 명)
   */
  static async addRoleMembers(
    masterObjid: number,
    userIds: string[],
    writer: string
  ): Promise<void> {
    try {
      // 이미 존재하는 멤버 제외
      const existingSql = `
        SELECT user_id 
        FROM authority_sub_user 
        WHERE master_objid = $1 AND user_id = ANY($2)
      `;
      const existing = await query<{ user_id: string }>(existingSql, [
        masterObjid,
        userIds,
      ]);
      const existingIds = new Set(
        existing.map((row: { user_id: string }) => row.user_id)
      );

      const newUserIds = userIds.filter((userId) => !existingIds.has(userId));

      if (newUserIds.length === 0) {
        logger.info("추가할 신규 멤버가 없습니다", { masterObjid, userIds });
        return;
      }

      // 배치 삽입
      const values = newUserIds
        .map(
          (_, index) =>
            `(nextval('seq_authority_sub_user'), $1, $${index + 2}, $${newUserIds.length + 2}, NOW())`
        )
        .join(", ");

      const sql = `
        INSERT INTO authority_sub_user (objid, master_objid, user_id, writer, regdate)
        VALUES ${values}
      `;

      await query(sql, [masterObjid, ...newUserIds, writer]);

      // 히스토리 기록
      for (const userId of newUserIds) {
        await this.insertAuthorityHistory(masterObjid, userId, "ADD", writer);
      }

      logger.info("권한 그룹 멤버 추가 성공", {
        masterObjid,
        count: newUserIds.length,
      });
    } catch (error) {
      logger.error("권한 그룹 멤버 추가 실패", { error, masterObjid, userIds });
      throw error;
    }
  }

  /**
   * 권한 그룹 멤버 제거 (여러 명)
   */
  static async removeRoleMembers(
    masterObjid: number,
    userIds: string[],
    writer: string
  ): Promise<void> {
    try {
      await query(
        "DELETE FROM authority_sub_user WHERE master_objid = $1 AND user_id = ANY($2)",
        [masterObjid, userIds]
      );

      // 히스토리 기록
      for (const userId of userIds) {
        await this.insertAuthorityHistory(
          masterObjid,
          userId,
          "REMOVE",
          writer
        );
      }

      logger.info("권한 그룹 멤버 제거 성공", {
        masterObjid,
        count: userIds.length,
      });
    } catch (error) {
      logger.error("권한 그룹 멤버 제거 실패", { error, masterObjid, userIds });
      throw error;
    }
  }

  /**
   * 권한 히스토리 기록
   */
  private static async insertAuthorityHistory(
    masterObjid: number,
    userId: string,
    historyType: "ADD" | "REMOVE",
    writer: string
  ): Promise<void> {
    try {
      const sql = `
        INSERT INTO authority_master_history 
        (objid, parent_objid, parent_name, parent_code, user_id, active, history_type, writer, reg_date)
        SELECT
          nextval('seq_authority_master'),
          $1,
          am.auth_name,
          am.auth_code,
          $2,
          am.status,
          $3,
          $4,
          NOW()
        FROM authority_master am
        WHERE am.objid = $1
      `;

      await query(sql, [masterObjid, userId, historyType, writer]);
    } catch (error) {
      logger.error("권한 히스토리 기록 실패", {
        error,
        masterObjid,
        userId,
        historyType,
      });
      // 히스토리 기록 실패는 메인 작업을 중단하지 않음
    }
  }

  /**
   * 메뉴 권한 목록 조회
   */
  static async getMenuPermissions(
    authObjid: number
  ): Promise<MenuPermission[]> {
    try {
      const sql = `
        SELECT
          rma.objid,
          rma.menu_objid AS "menuObjid",
          rma.auth_objid AS "authObjid",
          mi.menu_name_kor AS "menuName",
          mi.menu_code AS "menuCode",
          mi.menu_url AS "menuUrl",
          rma.create_yn AS "createYn",
          rma.read_yn AS "readYn",
          rma.update_yn AS "updateYn",
          rma.delete_yn AS "deleteYn",
          rma.execute_yn AS "executeYn",
          rma.export_yn AS "exportYn",
          rma.writer,
          rma.regdate
        FROM rel_menu_auth rma
        LEFT JOIN menu_info mi ON rma.menu_objid = mi.objid
        WHERE rma.auth_objid = $1
        ORDER BY mi.menu_name_kor
      `;

      const result = await query<MenuPermission>(sql, [authObjid]);
      return result;
    } catch (error) {
      logger.error("메뉴 권한 조회 실패", { error, authObjid });
      throw error;
    }
  }

  /**
   * 메뉴 권한 설정 (여러 메뉴)
   */
  static async setMenuPermissions(
    authObjid: number,
    permissions: Array<{
      menuObjid: number;
      createYn: string;
      readYn: string;
      updateYn: string;
      deleteYn: string;
    }>,
    writer: string
  ): Promise<void> {
    try {
      // 기존 권한 삭제
      await query("DELETE FROM rel_menu_auth WHERE auth_objid = $1", [
        authObjid,
      ]);

      // 새로운 권한 삽입
      if (permissions.length > 0) {
        const values = permissions
          .map(
            (_, index) =>
              `(nextval('seq_rel_menu_auth'), $${index * 5 + 2}, $1, $${index * 5 + 3}, $${index * 5 + 4}, $${index * 5 + 5}, $${index * 5 + 6}, $${permissions.length * 5 + 2}, NOW())`
          )
          .join(", ");

        const params = permissions.flatMap((p) => [
          p.menuObjid,
          p.createYn,
          p.readYn,
          p.updateYn,
          p.deleteYn,
        ]);

        const sql = `
          INSERT INTO rel_menu_auth (objid, menu_objid, auth_objid, create_yn, read_yn, update_yn, delete_yn, writer, regdate)
          VALUES ${values}
        `;

        await query(sql, [authObjid, ...params, writer]);
      }

      logger.info("메뉴 권한 설정 성공", {
        authObjid,
        count: permissions.length,
      });
    } catch (error) {
      logger.error("메뉴 권한 설정 실패", { error, authObjid, permissions });
      throw error;
    }
  }

  /**
   * 사용자가 속한 권한 그룹 목록 조회
   */
  static async getUserRoleGroups(
    userId: string,
    companyCode: string
  ): Promise<RoleGroup[]> {
    try {
      const sql = `
        SELECT
          am.objid,
          am.auth_name AS "authName",
          am.auth_code AS "authCode",
          am.company_code AS "companyCode",
          am.status,
          am.writer,
          am.regdate
        FROM authority_master am
        JOIN authority_sub_user asu ON am.objid = asu.master_objid
        WHERE asu.user_id = $1
          AND am.company_code = $2
          AND am.status = 'active'
        ORDER BY am.auth_name
      `;

      const result = await query<RoleGroup>(sql, [userId, companyCode]);
      return result;
    } catch (error) {
      logger.error("사용자 권한 그룹 조회 실패", {
        error,
        userId,
        companyCode,
      });
      throw error;
    }
  }

  /**
   * 전체 메뉴 목록 조회 (권한 설정용)
   * 
   * @param companyCode - 회사 코드
   *   - undefined: 최고 관리자 - 모든 회사의 모든 메뉴 조회
   *   - "*": 최고 관리자의 공통 메뉴만 조회 (최고 관리자 전용)
   *   - "COMPANY_X": 해당 회사 메뉴만 조회 (공통 메뉴 제외)
   * 
   * 중요: 
   * - 공통 메뉴(company_code = "*")는 최고 관리자 전용 메뉴입니다.
   * - menu_type = 2 (화면)는 제외하고 메뉴만 조회합니다.
   */
  static async getAllMenus(companyCode?: string): Promise<any[]> {
    try {
      logger.info("🔍 전체 메뉴 목록 조회 시작", { companyCode });

      let whereConditions: string[] = [
        "status = 'active'",
        "menu_type != 2" // 화면 제외, 메뉴만 조회
      ];
      const params: any[] = [];
      let paramIndex = 1;

      // 회사 코드에 따른 필터링
      if (companyCode === undefined) {
        // 최고 관리자: 모든 메뉴 조회
        logger.info("📋 최고 관리자 모드: 모든 메뉴 조회");
      } else if (companyCode === "*") {
        // 공통 메뉴만 조회
        whereConditions.push(`company_code = $${paramIndex}`);
        params.push("*");
        paramIndex++;
        logger.info("📋 공통 메뉴만 조회");
      } else {
        // 특정 회사: 해당 회사 메뉴 + 공통 메뉴 조회
        whereConditions.push(`(company_code = $${paramIndex} OR company_code = '*')`);
        params.push(companyCode);
        paramIndex++;
        logger.info("📋 회사별 필터 적용 (해당 회사 + 공통 메뉴)", { companyCode });
      }

      const whereClause = whereConditions.join(" AND ");

      const sql = `
        SELECT
          objid,
          menu_name_kor AS "menuName",
          menu_name_eng AS "menuNameEng",
          menu_code AS "menuCode",
          menu_url AS "menuUrl",
          CAST(menu_type AS TEXT) AS "menuType",
          parent_obj_id AS "parentObjid",
          seq AS "sortOrder",
          company_code AS "companyCode"
        FROM menu_info
        WHERE ${whereClause}
        ORDER BY 
          CASE 
            WHEN parent_obj_id = 0 OR parent_obj_id IS NULL THEN 0
            ELSE 1
          END,
          seq,
          menu_name_kor
      `;

      logger.info("🔍 SQL 쿼리 실행", {
        whereClause,
        params,
        sql: sql.substring(0, 200) + "...",
      });

      const result = await query<any>(sql, params);

      logger.info("✅ 메뉴 목록 조회 성공", {
        count: result.length,
        companyCode: companyCode || "전체",
        companyCodes: [...new Set(result.map((m) => m.companyCode))],
        menus: result.slice(0, 5).map((m) => ({
          objid: m.objid,
          name: m.menuName,
          code: m.menuCode,
          companyCode: m.companyCode,
        })),
      });

      return result;
    } catch (error) {
      logger.error("❌ 메뉴 목록 조회 실패", { error, companyCode });
      throw error;
    }
  }
}
