import { Request, Response } from "express";
import {
  CommonCodeService,
  CreateCategoryData,
  CreateCodeData,
} from "../services/commonCodeService";
import { AuthenticatedRequest } from "../types/auth";
import { logger } from "../utils/logger";
import { auditLogService, getClientIp } from "../services/auditLogService";

export class CommonCodeController {
  private commonCodeService: CommonCodeService;

  constructor() {
    this.commonCodeService = new CommonCodeService();
  }

  /**
   * 카테고리 목록 조회
   * GET /api/common-codes/categories
   */
  async getCategories(req: AuthenticatedRequest, res: Response) {
    try {
      const { search, isActive, page = "1", size = "20", menuObjid } = req.query;
      const userCompanyCode = req.user?.companyCode;
      const menuObjidNum = menuObjid ? Number(menuObjid) : undefined;

      const categories = await this.commonCodeService.getCategories(
        {
          search: search as string,
          isActive:
            isActive === "true"
              ? true
              : isActive === "false"
                ? false
                : undefined,
          page: parseInt(page as string),
          size: parseInt(size as string),
        },
        userCompanyCode,
        menuObjidNum
      );

      return res.json({
        success: true,
        data: categories.data,
        total: categories.total,
        message: "카테고리 목록 조회 성공",
      });
    } catch (error) {
      logger.error("카테고리 목록 조회 실패:", error);
      return res.status(500).json({
        success: false,
        message: "카테고리 목록 조회 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 카테고리별 코드 목록 조회
   * GET /api/common-codes/categories/:categoryCode/codes
   */
  async getCodes(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const { search, isActive, page, size, menuObjid } = req.query;
      const userCompanyCode = req.user?.companyCode;
      const menuObjidNum = menuObjid ? Number(menuObjid) : undefined;

      const result = await this.commonCodeService.getCodes(
        categoryCode,
        {
          search: search as string,
          isActive:
            isActive === "true"
              ? true
              : isActive === "false"
                ? false
                : undefined,
          page: page ? parseInt(page as string) : undefined,
          size: size ? parseInt(size as string) : undefined,
        },
        userCompanyCode,
        menuObjidNum
      );

      // 프론트엔드가 기대하는 형식으로 데이터 변환
      const transformedData = result.data.map((code: any) => ({
        // 새로운 필드명 (카멜케이스)
        codeValue: code.code_value,
        codeName: code.code_name,
        codeNameEng: code.code_name_eng,
        description: code.description,
        sortOrder: code.sort_order,
        isActive: code.is_active,
        useYn: code.is_active,
        companyCode: code.company_code,
        parentCodeValue: code.parent_code_value, // 계층구조: 부모 코드값
        depth: code.depth, // 계층구조: 깊이

        // 기존 필드명도 유지 (하위 호환성)
        code_category: code.code_category,
        code_value: code.code_value,
        code_name: code.code_name,
        code_name_eng: code.code_name_eng,
        sort_order: code.sort_order,
        is_active: code.is_active,
        company_code: code.company_code,
        parent_code_value: code.parent_code_value, // 계층구조: 부모 코드값
        // depth는 위에서 이미 정의됨 (snake_case와 camelCase 동일)
        created_date: code.created_date,
        created_by: code.created_by,
        updated_date: code.updated_date,
        updated_by: code.updated_by,
      }));

      return res.json({
        success: true,
        data: transformedData,
        total: result.total,
        message: `코드 목록 조회 성공 (${categoryCode})`,
      });
    } catch (error) {
      logger.error(`코드 목록 조회 실패 (${req.params.categoryCode}):`, error);
      return res.status(500).json({
        success: false,
        message: "코드 목록 조회 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 카테고리 생성
   * POST /api/common-codes/categories
   */
  async createCategory(req: AuthenticatedRequest, res: Response) {
    try {
      const categoryData: CreateCategoryData = req.body;
      const userId = req.user?.userId || "SYSTEM";
      const companyCode = req.user?.companyCode || "*";
      const menuObjid = req.body.menuObjid;

      // 입력값 검증
      if (!categoryData.categoryCode || !categoryData.categoryName) {
        return res.status(400).json({
          success: false,
          message: "카테고리 코드와 이름은 필수입니다.",
        });
      }

      if (!menuObjid) {
        return res.status(400).json({
          success: false,
          message: "메뉴 OBJID는 필수입니다.",
        });
      }

      const category = await this.commonCodeService.createCategory(
        categoryData,
        userId,
        companyCode,
        Number(menuObjid)
      );

      auditLogService.log({
        companyCode: companyCode || "",
        userId: userId || "",
        action: "CREATE",
        resourceType: "CODE_CATEGORY",
        resourceId: category?.category_code,
        resourceName: category?.category_name || categoryData.categoryName,
        summary: `코드 카테고리 "${category?.category_name || categoryData.categoryName}" 생성`,
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.status(201).json({
        success: true,
        data: category,
        message: "카테고리 생성 성공",
      });
    } catch (error) {
      logger.error("카테고리 생성 실패:", error);

      // PostgreSQL 에러 처리
      if (
        (error as any)?.code === "23505" || // PostgreSQL unique_violation
        (error instanceof Error && error.message.includes("Unique constraint"))
      ) {
        return res.status(409).json({
          success: false,
          message: "이미 존재하는 카테고리 코드입니다.",
        });
      }

      return res.status(500).json({
        success: false,
        message: "카테고리 생성 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 카테고리 수정
   * PUT /api/common-codes/categories/:categoryCode
   */
  async updateCategory(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const categoryData: Partial<CreateCategoryData> = req.body;
      const userId = req.user?.userId || "SYSTEM";
      const companyCode = req.user?.companyCode;

      const category = await this.commonCodeService.updateCategory(
        categoryCode,
        categoryData,
        userId,
        companyCode
      );

      auditLogService.log({
        companyCode: companyCode || "",
        userId: userId || "",
        action: "UPDATE",
        resourceType: "CODE_CATEGORY",
        resourceId: categoryCode,
        resourceName: category?.category_name,
        summary: `코드 카테고리 "${categoryCode}" 수정`,
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.json({
        success: true,
        data: category,
        message: "카테고리 수정 성공",
      });
    } catch (error) {
      logger.error(`카테고리 수정 실패 (${req.params.categoryCode}):`, error);

      if (
        error instanceof Error &&
        error.message.includes("Record to update not found")
      ) {
        return res.status(404).json({
          success: false,
          message: "존재하지 않는 카테고리입니다.",
        });
      }

      return res.status(500).json({
        success: false,
        message: "카테고리 수정 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 카테고리 삭제
   * DELETE /api/common-codes/categories/:categoryCode
   */
  async deleteCategory(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const companyCode = req.user?.companyCode;

      await this.commonCodeService.deleteCategory(categoryCode, companyCode);

      auditLogService.log({
        companyCode: companyCode || "",
        userId: req.user?.userId || "",
        action: "DELETE",
        resourceType: "CODE_CATEGORY",
        resourceId: categoryCode,
        summary: `코드 카테고리 "${categoryCode}" 삭제`,
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.json({
        success: true,
        message: "카테고리 삭제 성공",
      });
    } catch (error) {
      logger.error(`카테고리 삭제 실패 (${req.params.categoryCode}):`, error);

      if (
        error instanceof Error &&
        error.message.includes("Record to delete does not exist")
      ) {
        return res.status(404).json({
          success: false,
          message: "존재하지 않는 카테고리입니다.",
        });
      }

      return res.status(500).json({
        success: false,
        message: "카테고리 삭제 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 코드 생성
   * POST /api/common-codes/categories/:categoryCode/codes
   */
  async createCode(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const codeData: CreateCodeData = req.body;
      const userId = req.user?.userId || "SYSTEM";
      const companyCode = req.user?.companyCode || "*";
      const menuObjid = req.body.menuObjid;

      // 입력값 검증
      if (!codeData.codeValue || !codeData.codeName) {
        return res.status(400).json({
          success: false,
          message: "코드값과 코드명은 필수입니다.",
        });
      }

      // menuObjid가 없으면 공통코드관리 메뉴의 기본 OBJID 사용 (전역 코드)
      // 공통코드관리 메뉴 OBJID: 1757401858940
      const DEFAULT_CODE_MANAGEMENT_MENU_OBJID = 1757401858940;
      const effectiveMenuObjid = menuObjid ? Number(menuObjid) : DEFAULT_CODE_MANAGEMENT_MENU_OBJID;

      const code = await this.commonCodeService.createCode(
        categoryCode,
        codeData,
        userId,
        companyCode,
        effectiveMenuObjid
      );

      auditLogService.log({
        companyCode: companyCode || "",
        userId: userId || "",
        action: "CREATE",
        resourceType: "CODE",
        resourceId: codeData.codeValue,
        resourceName: codeData.codeName,
        summary: `코드 "${codeData.codeName}" (${categoryCode}) 생성`,
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.status(201).json({
        success: true,
        data: code,
        message: "코드 생성 성공",
      });
    } catch (error) {
      logger.error(`코드 생성 실패 (${req.params.categoryCode}):`, error);

      if (
        error instanceof Error &&
        error.message.includes("Unique constraint")
      ) {
        return res.status(409).json({
          success: false,
          message: "이미 존재하는 코드값입니다.",
        });
      }

      return res.status(500).json({
        success: false,
        message: "코드 생성 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 코드 수정
   * PUT /api/common-codes/categories/:categoryCode/codes/:codeValue
   */
  async updateCode(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode, codeValue } = req.params;
      const codeData: Partial<CreateCodeData> = req.body;
      const userId = req.user?.userId || "SYSTEM";
      const companyCode = req.user?.companyCode;

      const code = await this.commonCodeService.updateCode(
        categoryCode,
        codeValue,
        codeData,
        userId,
        companyCode
      );

      auditLogService.log({
        companyCode: companyCode || "",
        userId: userId || "",
        action: "UPDATE",
        resourceType: "CODE",
        resourceId: codeValue,
        resourceName: codeData.codeName || codeValue,
        tableName: "code_info",
        summary: `코드 "${categoryCode}.${codeValue}" 수정`,
        changes: { after: codeData },
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      return res.json({
        success: true,
        data: code,
        message: "코드 수정 성공",
      });
    } catch (error) {
      logger.error(
        `코드 수정 실패 (${req.params.categoryCode}.${req.params.codeValue}):`,
        error
      );

      if (
        error instanceof Error &&
        error.message.includes("Record to update not found")
      ) {
        return res.status(404).json({
          success: false,
          message: "존재하지 않는 코드입니다.",
        });
      }

      return res.status(500).json({
        success: false,
        message: "코드 수정 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 코드 삭제
   * DELETE /api/common-codes/categories/:categoryCode/codes/:codeValue
   */
  async deleteCode(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode, codeValue } = req.params;
      const companyCode = req.user?.companyCode;

      await this.commonCodeService.deleteCode(
        categoryCode,
        codeValue,
        companyCode
      );

      auditLogService.log({
        companyCode: companyCode || "",
        userId: req.user?.userId || "",
        action: "DELETE",
        resourceType: "CODE",
        resourceId: codeValue,
        tableName: "code_info",
        summary: `코드 "${categoryCode}.${codeValue}" 삭제`,
        changes: { before: { categoryCode, codeValue } },
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      return res.json({
        success: true,
        message: "코드 삭제 성공",
      });
    } catch (error) {
      logger.error(
        `코드 삭제 실패 (${req.params.categoryCode}.${req.params.codeValue}):`,
        error
      );

      if (
        error instanceof Error &&
        error.message.includes("Record to delete does not exist")
      ) {
        return res.status(404).json({
          success: false,
          message: "존재하지 않는 코드입니다.",
        });
      }

      return res.status(500).json({
        success: false,
        message: "코드 삭제 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 카테고리별 옵션 조회 (화면관리용)
   * GET /api/common-codes/categories/:categoryCode/options
   */
  async getCodeOptions(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const userCompanyCode = req.user?.companyCode;

      const options = await this.commonCodeService.getCodeOptions(
        categoryCode,
        userCompanyCode
      );

      return res.json({
        success: true,
        data: options,
        message: `코드 옵션 조회 성공 (${categoryCode})`,
      });
    } catch (error) {
      logger.error(`코드 옵션 조회 실패 (${req.params.categoryCode}):`, error);
      return res.status(500).json({
        success: false,
        message: "코드 옵션 조회 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 코드 순서 변경
   * PUT /api/common-codes/categories/:categoryCode/codes/reorder
   */
  async reorderCodes(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const { codes } = req.body as {
        codes: Array<{ codeValue: string; sortOrder: number }>;
      };
      const userId = req.user?.userId || "SYSTEM";

      if (!codes || !Array.isArray(codes)) {
        return res.status(400).json({
          success: false,
          message: "코드 순서 정보가 올바르지 않습니다.",
        });
      }

      await this.commonCodeService.reorderCodes(categoryCode, codes, userId);

      return res.json({
        success: true,
        message: "코드 순서 변경 성공",
      });
    } catch (error) {
      logger.error(`코드 순서 변경 실패 (${req.params.categoryCode}):`, error);
      return res.status(500).json({
        success: false,
        message: "코드 순서 변경 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 카테고리 중복 검사 (회사별)
   * GET /api/common-codes/categories/check-duplicate?field=categoryCode&value=USER_STATUS&excludeCode=OLD_CODE
   */
  async checkCategoryDuplicate(req: AuthenticatedRequest, res: Response) {
    try {
      const { field, value, excludeCode } = req.query;
      const userCompanyCode = req.user?.companyCode;

      // 입력값 검증
      if (!field || !value) {
        return res.status(400).json({
          success: false,
          message: "field와 value 파라미터가 필요합니다.",
        });
      }

      const validFields = ["categoryCode", "categoryName", "categoryNameEng"];
      if (!validFields.includes(field as string)) {
        return res.status(400).json({
          success: false,
          message:
            "field는 categoryCode, categoryName, categoryNameEng 중 하나여야 합니다.",
        });
      }

      const result = await this.commonCodeService.checkCategoryDuplicate(
        field as "categoryCode" | "categoryName" | "categoryNameEng",
        value as string,
        excludeCode as string,
        userCompanyCode
      );

      return res.json({
        success: true,
        data: {
          ...result,
          field,
          value,
        },
        message: "카테고리 중복 검사 완료",
      });
    } catch (error) {
      logger.error("카테고리 중복 검사 실패:", error);
      return res.status(500).json({
        success: false,
        message: "카테고리 중복 검사 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 코드 중복 검사 (회사별)
   * GET /api/common-codes/categories/:categoryCode/codes/check-duplicate?field=codeValue&value=ACTIVE&excludeCode=OLD_CODE
   */
  async checkCodeDuplicate(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const { field, value, excludeCode } = req.query;
      const userCompanyCode = req.user?.companyCode;

      // 입력값 검증
      if (!field || !value) {
        return res.status(400).json({
          success: false,
          message: "field와 value 파라미터가 필요합니다.",
        });
      }

      const validFields = ["codeValue", "codeName", "codeNameEng"];
      if (!validFields.includes(field as string)) {
        return res.status(400).json({
          success: false,
          message:
            "field는 codeValue, codeName, codeNameEng 중 하나여야 합니다.",
        });
      }

      const result = await this.commonCodeService.checkCodeDuplicate(
        categoryCode,
        field as "codeValue" | "codeName" | "codeNameEng",
        value as string,
        excludeCode as string,
        userCompanyCode
      );

      return res.json({
        success: true,
        data: {
          ...result,
          categoryCode,
          field,
          value,
        },
        message: "코드 중복 검사 완료",
      });
    } catch (error) {
      logger.error(`코드 중복 검사 실패 (${req.params.categoryCode}):`, error);
      return res.status(500).json({
        success: false,
        message: "코드 중복 검사 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 계층구조 코드 조회
   * GET /api/common-codes/categories/:categoryCode/hierarchy
   * Query: parentCodeValue (optional), depth (optional), menuObjid (optional)
   */
  async getHierarchicalCodes(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const { parentCodeValue, depth, menuObjid } = req.query;
      const userCompanyCode = req.user?.companyCode;
      const menuObjidNum = menuObjid ? Number(menuObjid) : undefined;

      // parentCodeValue가 빈 문자열이면 최상위 코드 조회
      const parentValue = parentCodeValue === '' || parentCodeValue === undefined 
        ? null 
        : parentCodeValue as string;

      const codes = await this.commonCodeService.getHierarchicalCodes(
        categoryCode,
        parentValue,
        depth ? parseInt(depth as string) : undefined,
        userCompanyCode,
        menuObjidNum
      );

      // 프론트엔드 형식으로 변환
      const transformedData = codes.map((code: any) => ({
        codeValue: code.code_value,
        codeName: code.code_name,
        codeNameEng: code.code_name_eng,
        description: code.description,
        sortOrder: code.sort_order,
        isActive: code.is_active,
        parentCodeValue: code.parent_code_value,
        depth: code.depth,
        // 기존 필드도 유지
        code_category: code.code_category,
        code_value: code.code_value,
        code_name: code.code_name,
        code_name_eng: code.code_name_eng,
        sort_order: code.sort_order,
        is_active: code.is_active,
        parent_code_value: code.parent_code_value,
      }));

      return res.json({
        success: true,
        data: transformedData,
        message: `계층구조 코드 조회 성공 (${categoryCode})`,
      });
    } catch (error) {
      logger.error(`계층구조 코드 조회 실패 (${req.params.categoryCode}):`, error);
      return res.status(500).json({
        success: false,
        message: "계층구조 코드 조회 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 코드 트리 조회
   * GET /api/common-codes/categories/:categoryCode/tree
   */
  async getCodeTree(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode } = req.params;
      const { menuObjid } = req.query;
      const userCompanyCode = req.user?.companyCode;
      const menuObjidNum = menuObjid ? Number(menuObjid) : undefined;

      const result = await this.commonCodeService.getCodeTree(
        categoryCode,
        userCompanyCode,
        menuObjidNum
      );

      return res.json({
        success: true,
        data: result,
        message: `코드 트리 조회 성공 (${categoryCode})`,
      });
    } catch (error) {
      logger.error(`코드 트리 조회 실패 (${req.params.categoryCode}):`, error);
      return res.status(500).json({
        success: false,
        message: "코드 트리 조회 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }

  /**
   * 자식 코드 존재 여부 확인
   * GET /api/common-codes/categories/:categoryCode/codes/:codeValue/has-children
   */
  async hasChildren(req: AuthenticatedRequest, res: Response) {
    try {
      const { categoryCode, codeValue } = req.params;
      const companyCode = req.user?.companyCode;

      const hasChildren = await this.commonCodeService.hasChildren(
        categoryCode,
        codeValue,
        companyCode
      );

      return res.json({
        success: true,
        data: { hasChildren },
        message: "자식 코드 확인 완료",
      });
    } catch (error) {
      logger.error(
        `자식 코드 확인 실패 (${req.params.categoryCode}.${req.params.codeValue}):`,
        error
      );
      return res.status(500).json({
        success: false,
        message: "자식 코드 확인 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "Unknown error",
      });
    }
  }
}
