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

export interface CodeCategory {
  category_code: string;
  category_name: string;
  category_name_eng?: string | null;
  description?: string | null;
  sort_order: number;
  is_active: string;
  company_code: string; // 추가
  created_date?: Date | null;
  created_by?: string | null;
  updated_date?: Date | null;
  updated_by?: string | null;
}

export interface CodeInfo {
  code_category: string;
  code_value: string;
  code_name: string;
  code_name_eng?: string | null;
  description?: string | null;
  sort_order: number;
  is_active: string;
  company_code: string;
  menu_objid?: number | null; // 메뉴 기반 코드 관리용
  parent_code_value?: string | null; // 계층구조: 부모 코드값
  depth?: number; // 계층구조: 깊이 (1, 2, 3단계)
  created_date?: Date | null;
  created_by?: string | null;
  updated_date?: Date | null;
  updated_by?: string | null;
}

export interface GetCategoriesParams {
  search?: string;
  isActive?: boolean;
  page?: number;
  size?: number;
}

export interface GetCodesParams {
  search?: string;
  isActive?: boolean;
  page?: number;
  size?: number;
}

export interface CreateCategoryData {
  categoryCode: string;
  categoryName: string;
  categoryNameEng?: string;
  description?: string;
  sortOrder?: number;
  isActive?: string;
}

export interface CreateCodeData {
  codeValue: string;
  codeName: string;
  codeNameEng?: string;
  description?: string;
  sortOrder?: number;
  isActive?: string;
  parentCodeValue?: string; // 계층구조: 부모 코드값
  depth?: number; // 계층구조: 깊이 (1, 2, 3단계)
}

export class CommonCodeService {
  /**
   * 카테고리 목록 조회
   */
  async getCategories(params: GetCategoriesParams, userCompanyCode?: string, menuObjid?: number) {
    try {
      const { search, isActive, page = 1, size = 20 } = params;

      const whereConditions: string[] = [];
      const values: any[] = [];
      let paramIndex = 1;

      // 메뉴별 필터링 (형제 메뉴 포함)
      if (menuObjid) {
        const { getSiblingMenuObjids } = await import('./menuService');
        const siblingMenuObjids = await getSiblingMenuObjids(menuObjid);
        whereConditions.push(`menu_objid = ANY($${paramIndex})`);
        values.push(siblingMenuObjids);
        paramIndex++;
        logger.info(`메뉴별 코드 카테고리 필터링: ${menuObjid}, 형제 메뉴: ${siblingMenuObjids.join(', ')}`);
      }

      // 회사별 필터링 (최고 관리자가 아닌 경우)
      // company_code = '*'인 공통 데이터도 함께 조회
      if (userCompanyCode && userCompanyCode !== "*") {
        whereConditions.push(`(company_code = $${paramIndex} OR company_code = '*')`);
        values.push(userCompanyCode);
        paramIndex++;
        logger.info(`회사별 코드 카테고리 필터링: ${userCompanyCode} (공통 데이터 포함)`);
      } else if (userCompanyCode === "*") {
        // 최고 관리자는 모든 데이터 조회 가능
        logger.info(`최고 관리자: 모든 코드 카테고리 조회`);
      }

      if (search) {
        whereConditions.push(
          `(category_name ILIKE $${paramIndex} OR category_code ILIKE $${paramIndex})`
        );
        values.push(`%${search}%`);
        paramIndex++;
      }

      if (isActive !== undefined) {
        whereConditions.push(`is_active = $${paramIndex++}`);
        values.push(isActive ? "Y" : "N");
      }

      const whereClause =
        whereConditions.length > 0
          ? `WHERE ${whereConditions.join(" AND ")}`
          : "";

      const offset = (page - 1) * size;

      // code_category 테이블에서만 조회 (comm_code 제거)
      const categories = await query<CodeCategory>(
        `SELECT * FROM code_category
         ${whereClause}
         ORDER BY sort_order ASC, category_code ASC
         LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
        [...values, size, offset]
      );

      // 전체 개수 조회
      const countResult = await queryOne<{ count: string }>(
        `SELECT COUNT(*) as count FROM code_category ${whereClause}`,
        values
      );

      const total = parseInt(countResult?.count || "0");

      logger.info(
        `카테고리 조회 완료: code_category ${categories.length}개, 전체: ${total}개 (회사: ${userCompanyCode || "전체"})`
      );

      return {
        data: categories,
        total,
      };
    } catch (error) {
      logger.error("카테고리 조회 중 오류:", error);
      throw error;
    }
  }

  /**
   * 카테고리별 코드 목록 조회
   */
  async getCodes(
    categoryCode: string,
    params: GetCodesParams,
    userCompanyCode?: string,
    menuObjid?: number
  ) {
    try {
      const { search, isActive, page = 1, size = 20 } = params;

      logger.info(`🔍 [getCodes] 코드 조회 시작:`, {
        categoryCode,
        menuObjid,
        hasMenuObjid: !!menuObjid,
        userCompanyCode,
        search,
        isActive,
        page,
        size,
      });

      const whereConditions: string[] = ["code_category = $1"];
      const values: any[] = [categoryCode];
      let paramIndex = 2;

      // 메뉴별 필터링 (형제 메뉴 포함)
      if (menuObjid) {
        const { getSiblingMenuObjids } = await import('./menuService');
        const siblingMenuObjids = await getSiblingMenuObjids(menuObjid);
        whereConditions.push(`menu_objid = ANY($${paramIndex})`);
        values.push(siblingMenuObjids);
        paramIndex++;
        logger.info(`📋 [getCodes] 메뉴별 코드 필터링:`, {
          menuObjid,
          siblingMenuObjids,
          siblingCount: siblingMenuObjids.length,
        });
      } else {
        logger.warn(`⚠️ [getCodes] menuObjid 없음 - 전역 코드 조회`);
      }

      // 회사별 필터링 (최고 관리자가 아닌 경우)
      if (userCompanyCode && userCompanyCode !== "*") {
        whereConditions.push(`company_code = $${paramIndex}`);
        values.push(userCompanyCode);
        paramIndex++;
        logger.info(`회사별 코드 필터링: ${userCompanyCode}`);
      } else if (userCompanyCode === "*") {
        logger.info(`최고 관리자: 모든 코드 조회`);
      }

      if (search) {
        whereConditions.push(
          `(code_name ILIKE $${paramIndex} OR code_value ILIKE $${paramIndex})`
        );
        values.push(`%${search}%`);
        paramIndex++;
      }

      if (isActive !== undefined) {
        whereConditions.push(`is_active = $${paramIndex++}`);
        values.push(isActive ? "Y" : "N");
      }

      const whereClause = `WHERE ${whereConditions.join(" AND ")}`;

      const offset = (page - 1) * size;

      logger.info(`📝 [getCodes] 실행할 쿼리:`, {
        whereClause,
        values,
        whereConditions,
        paramIndex,
      });

      // code_info 테이블에서만 코드 조회 (comm_code fallback 제거)
      const codes = await query<CodeInfo>(
        `SELECT * FROM code_info
         ${whereClause}
         ORDER BY sort_order ASC, code_value ASC
         LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
        [...values, size, offset]
      );

      // 전체 개수 조회
      const countResult = await queryOne<{ count: string }>(
        `SELECT COUNT(*) as count FROM code_info ${whereClause}`,
        values
      );

      const total = parseInt(countResult?.count || "0");

      logger.info(
        `코드 조회 완료: ${categoryCode} - ${codes.length}개, 전체: ${total}개 (회사: ${userCompanyCode || "전체"}, menuObjid: ${menuObjid || "없음"})`
      );

      return { data: codes, total };
    } catch (error) {
      logger.error(`코드 조회 중 오류 (${categoryCode}):`, error);
      throw error;
    }
  }

  /**
   * 카테고리 생성
   */
  async createCategory(
    data: CreateCategoryData,
    createdBy: string,
    companyCode: string,
    menuObjid: number
  ) {
    try {
      const category = await queryOne<CodeCategory>(
        `INSERT INTO code_category 
         (category_code, category_name, category_name_eng, description, sort_order, 
          is_active, menu_objid, company_code, created_by, updated_by, created_date, updated_date)
         VALUES ($1, $2, $3, $4, $5, 'Y', $6, $7, $8, $9, NOW(), NOW())
         RETURNING *`,
        [
          data.categoryCode,
          data.categoryName,
          data.categoryNameEng || null,
          data.description || null,
          data.sortOrder || 0,
          menuObjid,
          companyCode,
          createdBy,
          createdBy,
        ]
      );

      logger.info(
        `카테고리 생성 완료: ${data.categoryCode} (메뉴: ${menuObjid}, 회사: ${companyCode})`
      );
      return category;
    } catch (error) {
      logger.error("카테고리 생성 중 오류:", error);
      throw error;
    }
  }

  /**
   * 카테고리 수정
   */
  async updateCategory(
    categoryCode: string,
    data: Partial<CreateCategoryData>,
    updatedBy: string,
    companyCode?: string
  ) {
    try {
      // 디버깅: 받은 데이터 로그
      logger.info(`카테고리 수정 데이터:`, { categoryCode, data, companyCode });

      // 동적 UPDATE 쿼리 생성
      const updateFields: string[] = [
        "updated_by = $1",
        "updated_date = NOW()",
      ];
      const values: any[] = [updatedBy];
      let paramIndex = 2;

      if (data.categoryName !== undefined) {
        updateFields.push(`category_name = $${paramIndex++}`);
        values.push(data.categoryName);
      }
      if (data.categoryNameEng !== undefined) {
        updateFields.push(`category_name_eng = $${paramIndex++}`);
        values.push(data.categoryNameEng);
      }
      if (data.description !== undefined) {
        updateFields.push(`description = $${paramIndex++}`);
        values.push(data.description);
      }
      if (data.sortOrder !== undefined) {
        updateFields.push(`sort_order = $${paramIndex++}`);
        values.push(data.sortOrder);
      }
      if (data.isActive !== undefined) {
        const activeValue =
          typeof data.isActive === "boolean"
            ? data.isActive
              ? "Y"
              : "N"
            : data.isActive;
        updateFields.push(`is_active = $${paramIndex++}`);
        values.push(activeValue);
      }

      // WHERE 절 구성
      let whereClause = `WHERE category_code = $${paramIndex}`;
      values.push(categoryCode);

      // 회사 필터링 (최고 관리자가 아닌 경우)
      if (companyCode && companyCode !== "*") {
        paramIndex++;
        whereClause += ` AND company_code = $${paramIndex}`;
        values.push(companyCode);
      }

      const category = await queryOne<CodeCategory>(
        `UPDATE code_category 
         SET ${updateFields.join(", ")}
         ${whereClause}
         RETURNING *`,
        values
      );

      logger.info(
        `카테고리 수정 완료: ${categoryCode} (회사: ${companyCode || "전체"})`
      );
      return category;
    } catch (error) {
      logger.error(`카테고리 수정 중 오류 (${categoryCode}):`, error);
      throw error;
    }
  }

  /**
   * 카테고리 삭제
   */
  async deleteCategory(categoryCode: string, companyCode?: string) {
    try {
      let sql = `DELETE FROM code_category WHERE category_code = $1`;
      const values: any[] = [categoryCode];

      // 회사 필터링 (최고 관리자가 아닌 경우)
      if (companyCode && companyCode !== "*") {
        sql += ` AND company_code = $2`;
        values.push(companyCode);
      }

      await query(sql, values);

      logger.info(
        `카테고리 삭제 완료: ${categoryCode} (회사: ${companyCode || "전체"})`
      );
    } catch (error) {
      logger.error(`카테고리 삭제 중 오류 (${categoryCode}):`, error);
      throw error;
    }
  }

  /**
   * 코드 생성
   */
  async createCode(
    categoryCode: string,
    data: CreateCodeData,
    createdBy: string,
    companyCode: string,
    menuObjid: number
  ) {
    try {
      // 계층구조: depth 계산 (부모가 있으면 부모의 depth + 1, 없으면 1)
      let depth = 1;
      if (data.parentCodeValue) {
        const parentCode = await queryOne<CodeInfo>(
          `SELECT depth FROM code_info 
           WHERE code_category = $1 AND code_value = $2 AND company_code = $3`,
          [categoryCode, data.parentCodeValue, companyCode]
        );
        depth = parentCode ? (parentCode.depth || 1) + 1 : 1;
      }

      const code = await queryOne<CodeInfo>(
        `INSERT INTO code_info 
         (code_category, code_value, code_name, code_name_eng, description, sort_order, 
          is_active, menu_objid, company_code, parent_code_value, depth, created_by, updated_by, created_date, updated_date)
         VALUES ($1, $2, $3, $4, $5, $6, 'Y', $7, $8, $9, $10, $11, $12, NOW(), NOW())
         RETURNING *`,
        [
          categoryCode,
          data.codeValue,
          data.codeName,
          data.codeNameEng || null,
          data.description || null,
          data.sortOrder || 0,
          menuObjid,
          companyCode,
          data.parentCodeValue || null,
          depth,
          createdBy,
          createdBy,
        ]
      );

      logger.info(
        `코드 생성 완료: ${categoryCode}.${data.codeValue} (메뉴: ${menuObjid}, 회사: ${companyCode}, 부모: ${data.parentCodeValue || '없음'}, 깊이: ${depth})`
      );
      return code;
    } catch (error) {
      logger.error(
        `코드 생성 중 오류 (${categoryCode}.${data.codeValue}):`,
        error
      );
      throw error;
    }
  }

  /**
   * 코드 수정
   */
  async updateCode(
    categoryCode: string,
    codeValue: string,
    data: Partial<CreateCodeData>,
    updatedBy: string,
    companyCode?: string
  ) {
    try {
      // 디버깅: 받은 데이터 로그
      logger.info(`코드 수정 데이터:`, {
        categoryCode,
        codeValue,
        data,
        companyCode,
      });

      // 동적 UPDATE 쿼리 생성
      const updateFields: string[] = [
        "updated_by = $1",
        "updated_date = NOW()",
      ];
      const values: any[] = [updatedBy];
      let paramIndex = 2;

      if (data.codeName !== undefined) {
        updateFields.push(`code_name = $${paramIndex++}`);
        values.push(data.codeName);
      }
      if (data.codeNameEng !== undefined) {
        updateFields.push(`code_name_eng = $${paramIndex++}`);
        values.push(data.codeNameEng);
      }
      if (data.description !== undefined) {
        updateFields.push(`description = $${paramIndex++}`);
        values.push(data.description);
      }
      if (data.sortOrder !== undefined) {
        updateFields.push(`sort_order = $${paramIndex++}`);
        values.push(data.sortOrder);
      }
      if (data.isActive !== undefined) {
        const activeValue =
          typeof data.isActive === "boolean"
            ? data.isActive
              ? "Y"
              : "N"
            : data.isActive;
        updateFields.push(`is_active = $${paramIndex++}`);
        values.push(activeValue);
      }
      // 계층구조: 부모 코드값 수정
      if (data.parentCodeValue !== undefined) {
        updateFields.push(`parent_code_value = $${paramIndex++}`);
        values.push(data.parentCodeValue || null);
        
        // depth도 함께 업데이트
        let newDepth = 1;
        if (data.parentCodeValue) {
          const parentCode = await queryOne<CodeInfo>(
            `SELECT depth FROM code_info 
             WHERE code_category = $1 AND code_value = $2`,
            [categoryCode, data.parentCodeValue]
          );
          newDepth = parentCode ? (parentCode.depth || 1) + 1 : 1;
        }
        updateFields.push(`depth = $${paramIndex++}`);
        values.push(newDepth);
      }

      // WHERE 절 구성
      let whereClause = `WHERE code_category = $${paramIndex++} AND code_value = $${paramIndex}`;
      values.push(categoryCode, codeValue);

      // 회사 필터링 (최고 관리자가 아닌 경우)
      if (companyCode && companyCode !== "*") {
        paramIndex++;
        whereClause += ` AND company_code = $${paramIndex}`;
        values.push(companyCode);
      }

      const code = await queryOne<CodeInfo>(
        `UPDATE code_info 
         SET ${updateFields.join(", ")}
         ${whereClause}
         RETURNING *`,
        values
      );

      logger.info(
        `코드 수정 완료: ${categoryCode}.${codeValue} (회사: ${companyCode || "전체"})`
      );
      return code;
    } catch (error) {
      logger.error(`코드 수정 중 오류 (${categoryCode}.${codeValue}):`, error);
      throw error;
    }
  }

  /**
   * 코드 삭제
   */
  async deleteCode(
    categoryCode: string,
    codeValue: string,
    companyCode?: string
  ) {
    try {
      let sql = `DELETE FROM code_info WHERE code_category = $1 AND code_value = $2`;
      const values: any[] = [categoryCode, codeValue];

      // 회사 필터링 (최고 관리자가 아닌 경우)
      if (companyCode && companyCode !== "*") {
        sql += ` AND company_code = $3`;
        values.push(companyCode);
      }

      await query(sql, values);

      logger.info(
        `코드 삭제 완료: ${categoryCode}.${codeValue} (회사: ${companyCode || "전체"})`
      );
    } catch (error) {
      logger.error(`코드 삭제 중 오류 (${categoryCode}.${codeValue}):`, error);
      throw error;
    }
  }

  /**
   * 카테고리별 옵션 조회 (화면관리용)
   */
  async getCodeOptions(categoryCode: string, userCompanyCode?: string) {
    try {
      let sql = `SELECT code_value, code_name, code_name_eng, sort_order
         FROM code_info
         WHERE code_category = $1 AND is_active = 'Y'`;
      const values: any[] = [categoryCode];

      // 회사별 필터링 (최고 관리자가 아닌 경우)
      if (userCompanyCode && userCompanyCode !== "*") {
        sql += ` AND company_code = $2`;
        values.push(userCompanyCode);
        logger.info(`회사별 코드 옵션 필터링: ${userCompanyCode}`);
      } else if (userCompanyCode === "*") {
        logger.info(`최고 관리자: 모든 코드 옵션 조회`);
      }

      sql += ` ORDER BY sort_order ASC, code_value ASC`;

      const codes = await query<{
        code_value: string;
        code_name: string;
        code_name_eng: string | null;
        sort_order: number;
      }>(sql, values);

      const options = codes.map((code) => ({
        value: code.code_value,
        label: code.code_name,
        labelEng: code.code_name_eng,
      }));

      logger.info(
        `코드 옵션 조회 완료: ${categoryCode} - ${options.length}개 (회사: ${userCompanyCode || "전체"})`
      );
      return options;
    } catch (error) {
      logger.error(`코드 옵션 조회 중 오류 (${categoryCode}):`, error);
      throw error;
    }
  }

  /**
   * 코드 순서 변경
   */
  async reorderCodes(
    categoryCode: string,
    codes: Array<{ codeValue: string; sortOrder: number }>,
    updatedBy: string
  ) {
    try {
      // 먼저 존재하는 코드들을 확인
      const codeValues = codes.map((c) => c.codeValue);
      const placeholders = codeValues.map((_, i) => `$${i + 2}`).join(", ");

      const existingCodes = await query<{ code_value: string }>(
        `SELECT code_value FROM code_info 
         WHERE code_category = $1 AND code_value IN (${placeholders})`,
        [categoryCode, ...codeValues]
      );

      const existingCodeValues = existingCodes.map((c) => c.code_value);
      const validCodes = codes.filter((c) =>
        existingCodeValues.includes(c.codeValue)
      );

      if (validCodes.length === 0) {
        throw new Error(
          `카테고리 ${categoryCode}에 순서를 변경할 유효한 코드가 없습니다.`
        );
      }

      // 트랜잭션으로 업데이트
      await transaction(async (client) => {
        for (const { codeValue, sortOrder } of validCodes) {
          await client.query(
            `UPDATE code_info 
             SET sort_order = $1, updated_by = $2, updated_date = NOW()
             WHERE code_category = $3 AND code_value = $4`,
            [sortOrder, updatedBy, categoryCode, codeValue]
          );
        }
      });

      const skippedCodes = codes.filter(
        (c) => !existingCodeValues.includes(c.codeValue)
      );
      if (skippedCodes.length > 0) {
        logger.warn(
          `코드 순서 변경 시 존재하지 않는 코드들을 건너뜀: ${skippedCodes.map((c) => c.codeValue).join(", ")}`
        );
      }

      logger.info(
        `코드 순서 변경 완료: ${categoryCode} - ${validCodes.length}개 (전체 ${codes.length}개 중)`
      );
    } catch (error) {
      logger.error(`코드 순서 변경 중 오류 (${categoryCode}):`, error);
      throw error;
    }
  }

  /**
   * 카테고리 중복 검사 (회사별)
   */
  async checkCategoryDuplicate(
    field: "categoryCode" | "categoryName" | "categoryNameEng",
    value: string,
    excludeCategoryCode?: string,
    userCompanyCode?: string
  ): Promise<{ isDuplicate: boolean; message: string }> {
    try {
      if (!value || !value.trim()) {
        return {
          isDuplicate: false,
          message: "값을 입력해주세요.",
        };
      }

      const trimmedValue = value.trim();
      let whereCondition: any = {};

      // 필드별 검색 조건 설정
      switch (field) {
        case "categoryCode":
          whereCondition.category_code = trimmedValue;
          break;
        case "categoryName":
          whereCondition.category_name = trimmedValue;
          break;
        case "categoryNameEng":
          whereCondition.category_name_eng = trimmedValue;
          break;
      }

      // SQL 쿼리 생성
      let sql = "";
      const values: any[] = [];
      let paramIndex = 1;

      switch (field) {
        case "categoryCode":
          sql = `SELECT category_code FROM code_category WHERE category_code = $${paramIndex++}`;
          values.push(trimmedValue);
          break;
        case "categoryName":
          sql = `SELECT category_code FROM code_category WHERE category_name = $${paramIndex++}`;
          values.push(trimmedValue);
          break;
        case "categoryNameEng":
          sql = `SELECT category_code FROM code_category WHERE category_name_eng = $${paramIndex++}`;
          values.push(trimmedValue);
          break;
      }

      // 회사별 필터링 (최고 관리자가 아닌 경우)
      if (userCompanyCode && userCompanyCode !== "*") {
        sql += ` AND company_code = $${paramIndex++}`;
        values.push(userCompanyCode);
      }

      // 수정 시 자기 자신 제외
      if (excludeCategoryCode) {
        sql += ` AND category_code != $${paramIndex++}`;
        values.push(excludeCategoryCode);
      }

      sql += ` LIMIT 1`;

      const existingCategory = await queryOne<{ category_code: string }>(
        sql,
        values
      );

      const isDuplicate = !!existingCategory;
      const fieldNames = {
        categoryCode: "카테고리 코드",
        categoryName: "카테고리명",
        categoryNameEng: "카테고리 영문명",
      };

      logger.info(
        `카테고리 중복 검사: ${field}=${value}, 회사=${userCompanyCode}, 중복=${isDuplicate}`
      );

      return {
        isDuplicate,
        message: isDuplicate
          ? `이미 사용 중인 ${fieldNames[field]}입니다.`
          : `사용 가능한 ${fieldNames[field]}입니다.`,
      };
    } catch (error) {
      logger.error(`카테고리 중복 검사 중 오류 (${field}: ${value}):`, error);
      throw error;
    }
  }

  /**
   * 코드 중복 검사 (회사별)
   */
  async checkCodeDuplicate(
    categoryCode: string,
    field: "codeValue" | "codeName" | "codeNameEng",
    value: string,
    excludeCodeValue?: string,
    userCompanyCode?: string
  ): Promise<{ isDuplicate: boolean; message: string }> {
    try {
      if (!value || !value.trim()) {
        return {
          isDuplicate: false,
          message: "값을 입력해주세요.",
        };
      }

      const trimmedValue = value.trim();
      let whereCondition: any = {
        code_category: categoryCode,
      };

      // 필드별 검색 조건 설정
      switch (field) {
        case "codeValue":
          whereCondition.code_value = trimmedValue;
          break;
        case "codeName":
          whereCondition.code_name = trimmedValue;
          break;
        case "codeNameEng":
          whereCondition.code_name_eng = trimmedValue;
          break;
      }

      // SQL 쿼리 생성
      let sql =
        "SELECT code_value FROM code_info WHERE code_category = $1 AND ";
      const values: any[] = [categoryCode];
      let paramIndex = 2;

      switch (field) {
        case "codeValue":
          sql += `code_value = $${paramIndex++}`;
          values.push(trimmedValue);
          break;
        case "codeName":
          sql += `code_name = $${paramIndex++}`;
          values.push(trimmedValue);
          break;
        case "codeNameEng":
          sql += `code_name_eng = $${paramIndex++}`;
          values.push(trimmedValue);
          break;
      }

      // 회사별 필터링 (최고 관리자가 아닌 경우)
      if (userCompanyCode && userCompanyCode !== "*") {
        sql += ` AND company_code = $${paramIndex++}`;
        values.push(userCompanyCode);
      }

      // 수정 시 자기 자신 제외
      if (excludeCodeValue) {
        sql += ` AND code_value != $${paramIndex++}`;
        values.push(excludeCodeValue);
      }

      sql += ` LIMIT 1`;

      const existingCode = await queryOne<{ code_value: string }>(sql, values);

      const isDuplicate = !!existingCode;
      const fieldNames = {
        codeValue: "코드값",
        codeName: "코드명",
        codeNameEng: "코드 영문명",
      };

      logger.info(
        `코드 중복 검사: ${categoryCode}.${field}=${value}, 회사=${userCompanyCode}, 중복=${isDuplicate}`
      );

      return {
        isDuplicate,
        message: isDuplicate
          ? `이미 사용 중인 ${fieldNames[field]}입니다.`
          : `사용 가능한 ${fieldNames[field]}입니다.`,
      };
    } catch (error) {
      logger.error(
        `코드 중복 검사 중 오류 (${categoryCode}, ${field}: ${value}):`,
        error
      );
      throw error;
    }
  }

  /**
   * 계층구조 코드 조회 (특정 depth 또는 부모코드 기준)
   * @param categoryCode 카테고리 코드
   * @param parentCodeValue 부모 코드값 (없으면 최상위 코드만 조회)
   * @param depth 특정 깊이만 조회 (선택)
   */
  async getHierarchicalCodes(
    categoryCode: string,
    parentCodeValue?: string | null,
    depth?: number,
    userCompanyCode?: string,
    menuObjid?: number
  ) {
    try {
      const whereConditions: string[] = ["code_category = $1", "is_active = 'Y'"];
      const values: any[] = [categoryCode];
      let paramIndex = 2;

      // 부모 코드값 필터링
      if (parentCodeValue === null || parentCodeValue === undefined) {
        // 최상위 코드 (부모가 없는 코드)
        whereConditions.push("(parent_code_value IS NULL OR parent_code_value = '')");
      } else if (parentCodeValue !== '') {
        whereConditions.push(`parent_code_value = $${paramIndex}`);
        values.push(parentCodeValue);
        paramIndex++;
      }

      // 특정 깊이 필터링
      if (depth !== undefined) {
        whereConditions.push(`depth = $${paramIndex}`);
        values.push(depth);
        paramIndex++;
      }

      // 메뉴별 필터링 (형제 메뉴 포함)
      if (menuObjid) {
        const { getSiblingMenuObjids } = await import('./menuService');
        const siblingMenuObjids = await getSiblingMenuObjids(menuObjid);
        whereConditions.push(`menu_objid = ANY($${paramIndex})`);
        values.push(siblingMenuObjids);
        paramIndex++;
      }

      // 회사별 필터링
      if (userCompanyCode && userCompanyCode !== "*") {
        whereConditions.push(`company_code = $${paramIndex}`);
        values.push(userCompanyCode);
        paramIndex++;
      }

      const whereClause = `WHERE ${whereConditions.join(" AND ")}`;

      const codes = await query<CodeInfo>(
        `SELECT * FROM code_info
         ${whereClause}
         ORDER BY sort_order ASC, code_value ASC`,
        values
      );

      logger.info(
        `계층구조 코드 조회: ${categoryCode}, 부모: ${parentCodeValue || '최상위'}, 깊이: ${depth || '전체'} - ${codes.length}개`
      );

      return codes;
    } catch (error) {
      logger.error(`계층구조 코드 조회 중 오류 (${categoryCode}):`, error);
      throw error;
    }
  }

  /**
   * 계층구조 코드 트리 전체 조회 (카테고리 기준)
   */
  async getCodeTree(
    categoryCode: string,
    userCompanyCode?: string,
    menuObjid?: number
  ) {
    try {
      const whereConditions: string[] = ["code_category = $1", "is_active = 'Y'"];
      const values: any[] = [categoryCode];
      let paramIndex = 2;

      // 메뉴별 필터링 (형제 메뉴 포함)
      if (menuObjid) {
        const { getSiblingMenuObjids } = await import('./menuService');
        const siblingMenuObjids = await getSiblingMenuObjids(menuObjid);
        whereConditions.push(`menu_objid = ANY($${paramIndex})`);
        values.push(siblingMenuObjids);
        paramIndex++;
      }

      // 회사별 필터링
      if (userCompanyCode && userCompanyCode !== "*") {
        whereConditions.push(`company_code = $${paramIndex}`);
        values.push(userCompanyCode);
        paramIndex++;
      }

      const whereClause = `WHERE ${whereConditions.join(" AND ")}`;

      const allCodes = await query<CodeInfo>(
        `SELECT * FROM code_info
         ${whereClause}
         ORDER BY depth ASC, sort_order ASC, code_value ASC`,
        values
      );

      // 트리 구조로 변환
      const buildTree = (codes: CodeInfo[], parentValue: string | null = null): any[] => {
        return codes
          .filter(code => {
            const codeParent = code.parent_code_value || null;
            return codeParent === parentValue;
          })
          .map(code => ({
            ...code,
            children: buildTree(codes, code.code_value)
          }));
      };

      const tree = buildTree(allCodes);

      logger.info(
        `코드 트리 조회 완료: ${categoryCode} - 전체 ${allCodes.length}개`
      );

      return {
        flat: allCodes,
        tree
      };
    } catch (error) {
      logger.error(`코드 트리 조회 중 오류 (${categoryCode}):`, error);
      throw error;
    }
  }

  /**
   * 자식 코드가 있는지 확인
   */
  async hasChildren(
    categoryCode: string,
    codeValue: string,
    companyCode?: string
  ): Promise<boolean> {
    try {
      let sql = `SELECT COUNT(*) as count FROM code_info 
                 WHERE code_category = $1 AND parent_code_value = $2`;
      const values: any[] = [categoryCode, codeValue];

      if (companyCode && companyCode !== "*") {
        sql += ` AND company_code = $3`;
        values.push(companyCode);
      }

      const result = await queryOne<{ count: string }>(sql, values);
      const count = parseInt(result?.count || "0");

      return count > 0;
    } catch (error) {
      logger.error(`자식 코드 확인 중 오류 (${categoryCode}.${codeValue}):`, error);
      throw error;
    }
  }
}
