/**
 * 채번 규칙 관리 컨트롤러
 */

import { Router, Response } from "express";
import {
  authenticateToken,
  AuthenticatedRequest,
} from "../middleware/authMiddleware";
import { numberingRuleService } from "../services/numberingRuleService";
import { logger } from "../utils/logger";
import { auditLogService, getClientIp } from "../services/auditLogService";

const router = Router();

// 규칙 목록 조회 (전체)
router.get(
  "/",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;

    try {
      const rules = await numberingRuleService.getRuleList(companyCode);
      return res.json({ success: true, data: rules });
    } catch (error: any) {
      logger.error("규칙 목록 조회 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 메뉴별 사용 가능한 규칙 조회
router.get(
  "/available/:menuObjid?",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const menuObjid = req.params.menuObjid
      ? parseInt(req.params.menuObjid)
      : undefined;

    logger.info("메뉴별 채번 규칙 조회 요청", { menuObjid, companyCode });

    try {
      const rules = await numberingRuleService.getAvailableRulesForMenu(
        companyCode,
        menuObjid
      );

      logger.info("✅ 메뉴별 채번 규칙 조회 성공 (컨트롤러)", {
        companyCode,
        menuObjid,
        rulesCount: rules.length,
      });

      return res.json({ success: true, data: rules });
    } catch (error: any) {
      logger.error("❌ 메뉴별 사용 가능한 규칙 조회 실패 (컨트롤러)", {
        error: error.message,
        errorCode: error.code,
        errorStack: error.stack,
        companyCode,
        menuObjid,
      });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 화면용 채번 규칙 조회 (테이블 기반 필터링 - 간소화)
router.get(
  "/available-for-screen",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { tableName } = req.query;

    try {
      // tableName 필수 검증
      if (!tableName || typeof tableName !== "string") {
        return res.status(400).json({
          success: false,
          error: "tableName is required",
        });
      }

      const rules = await numberingRuleService.getAvailableRulesForScreen(
        companyCode,
        tableName
      );

      logger.info("화면용 채번 규칙 조회 성공", {
        companyCode,
        tableName,
        count: rules.length,
      });

      return res.json({ success: true, data: rules });
    } catch (error: any) {
      logger.error("화면용 채번 규칙 조회 실패", {
        error: error.message,
        tableName,
      });
      return res.status(500).json({
        success: false,
        error: error.message,
      });
    }
  }
);

// 특정 규칙 조회
router.get(
  "/:ruleId",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { ruleId } = req.params;

    try {
      const rule = await numberingRuleService.getRuleById(ruleId, companyCode);
      if (!rule) {
        return res
          .status(404)
          .json({ success: false, error: "규칙을 찾을 수 없습니다" });
      }
      return res.json({ success: true, data: rule });
    } catch (error: any) {
      logger.error("규칙 조회 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 규칙 생성
router.post(
  "/",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const userId = req.user!.userId;
    const ruleConfig = req.body;

    logger.info("🔍 [POST /numbering-rules] 채번 규칙 생성 요청:", {
      companyCode,
      userId,
      ruleId: ruleConfig.ruleId,
      ruleName: ruleConfig.ruleName,
      scopeType: ruleConfig.scopeType,
      menuObjid: ruleConfig.menuObjid,
      tableName: ruleConfig.tableName,
      partsCount: ruleConfig.parts?.length,
    });

    try {
      if (!ruleConfig.ruleId || !ruleConfig.ruleName) {
        return res
          .status(400)
          .json({ success: false, error: "규칙 ID와 규칙명은 필수입니다" });
      }

      if (!Array.isArray(ruleConfig.parts) || ruleConfig.parts.length === 0) {
        return res
          .status(400)
          .json({
            success: false,
            error: "최소 1개 이상의 규칙 파트가 필요합니다",
          });
      }

      // 🆕 scopeType이 'table'인 경우 tableName 필수 체크
      if (ruleConfig.scopeType === "table") {
        if (!ruleConfig.tableName || ruleConfig.tableName.trim() === "") {
          return res.status(400).json({
            success: false,
            error: "테이블 범위 규칙은 테이블명(tableName)이 필수입니다",
          });
        }
      }

      const newRule = await numberingRuleService.createRule(
        ruleConfig,
        companyCode,
        userId
      );

      logger.info("✅ [POST /numbering-rules] 채번 규칙 생성 성공:", {
        ruleId: newRule.ruleId,
        menuObjid: newRule.menuObjid,
      });

      auditLogService.log({
        companyCode,
        userId,
        userName: req.user?.userName,
        action: "CREATE",
        resourceType: "NUMBERING_RULE",
        resourceId: String(newRule.ruleId),
        resourceName: ruleConfig.ruleName,
        summary: `채번 규칙 "${ruleConfig.ruleName}" 생성`,
        changes: { after: { ruleName: ruleConfig.ruleName, prefix: ruleConfig.prefix } },
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.status(201).json({ success: true, data: newRule });
    } catch (error: any) {
      if (error.code === "23505") {
        return res
          .status(409)
          .json({ success: false, error: "이미 존재하는 규칙 ID입니다" });
      }
      logger.error("❌ [POST /numbering-rules] 규칙 생성 실패:", {
        error: error.message,
        stack: error.stack,
        code: error.code,
      });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 규칙 수정
router.put(
  "/:ruleId",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { ruleId } = req.params;
    const updates = req.body;

    logger.info("채번 규칙 수정 요청", { ruleId, companyCode, updates });

    try {
      const beforeRule = await numberingRuleService.getRuleById(ruleId, companyCode);
      const updatedRule = await numberingRuleService.updateRule(
        ruleId,
        updates,
        companyCode
      );
      logger.info("채번 규칙 수정 성공", { ruleId, companyCode });

      auditLogService.log({
        companyCode,
        userId: req.user?.userId || "",
        userName: req.user?.userName,
        action: "UPDATE",
        resourceType: "NUMBERING_RULE",
        resourceId: ruleId,
        summary: `채번 규칙(ID:${ruleId}) 수정`,
        changes: {
          before: { ruleName: beforeRule?.ruleName, separator: beforeRule?.separator },
          after: updates,
        },
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.json({ success: true, data: updatedRule });
    } catch (error: any) {
      logger.error("채번 규칙 수정 실패", {
        ruleId,
        companyCode,
        error: error.message,
        stack: error.stack,
      });
      if (error.message.includes("찾을 수 없거나")) {
        return res.status(404).json({ success: false, error: error.message });
      }
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 규칙 삭제
router.delete(
  "/:ruleId",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { ruleId } = req.params;

    try {
      await numberingRuleService.deleteRule(ruleId, companyCode);

      auditLogService.log({
        companyCode,
        userId: req.user?.userId || "",
        userName: req.user?.userName,
        action: "DELETE",
        resourceType: "NUMBERING_RULE",
        resourceId: ruleId,
        summary: `채번 규칙(ID:${ruleId}) 삭제`,
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.json({ success: true, message: "규칙이 삭제되었습니다" });
    } catch (error: any) {
      if (error.message.includes("찾을 수 없거나")) {
        return res.status(404).json({ success: false, error: error.message });
      }
      logger.error("규칙 삭제 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 코드 미리보기 (순번 증가 없음)
router.post(
  "/:ruleId/preview",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { ruleId } = req.params;
    const { formData, manualInputValue } = req.body;

    try {
      const previewCode = await numberingRuleService.previewCode(
        ruleId,
        companyCode,
        formData,
        manualInputValue
      );
      return res.json({ success: true, data: { generatedCode: previewCode } });
    } catch (error: any) {
      logger.error("코드 미리보기 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 코드 할당 (저장 시점에 실제 순번 증가)
router.post(
  "/:ruleId/allocate",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { ruleId } = req.params;
    const { formData, userInputCode } = req.body; // 폼 데이터 + 사용자가 편집한 코드

    logger.info("코드 할당 요청", {
      ruleId,
      companyCode,
      hasFormData: !!formData,
      userInputCode,
    });

    try {
      const allocatedCode = await numberingRuleService.allocateCode(
        ruleId,
        companyCode,
        formData,
        userInputCode
      );
      logger.info("코드 할당 성공", { ruleId, allocatedCode });
      return res.json({
        success: true,
        data: { generatedCode: allocatedCode },
      });
    } catch (error: any) {
      logger.error("코드 할당 실패", {
        ruleId,
        companyCode,
        error: error.message,
      });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 코드 생성 (기존 호환성 유지, deprecated)
router.post(
  "/:ruleId/generate",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { ruleId } = req.params;

    try {
      const generatedCode = await numberingRuleService.generateCode(
        ruleId,
        companyCode
      );
      return res.json({ success: true, data: { generatedCode } });
    } catch (error: any) {
      logger.error("코드 생성 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 시퀀스 초기화
router.post(
  "/:ruleId/reset",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { ruleId } = req.params;

    try {
      await numberingRuleService.resetSequence(ruleId, companyCode);
      return res.json({ success: true, message: "시퀀스가 초기화되었습니다" });
    } catch (error: any) {
      logger.error("시퀀스 초기화 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// 테이블+컬럼 기반 채번 규칙 조회 (메인 API)
router.get(
  "/by-column/:tableName/:columnName",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { tableName, columnName } = req.params;

    try {
      const rule = await numberingRuleService.getNumberingRuleByColumn(
        companyCode,
        tableName,
        columnName
      );
      return res.json({ success: true, data: rule });
    } catch (error: any) {
      logger.error("테이블+컬럼 기반 채번 규칙 조회 실패", {
        error: error.message,
      });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// ==================== 테스트 테이블용 API ====================

// [테스트] 테스트 테이블에서 채번 규칙 목록 조회
router.get(
  "/test/list/:menuObjid?",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const menuObjid = req.params.menuObjid
      ? parseInt(req.params.menuObjid)
      : undefined;

    logger.info("[테스트] 채번 규칙 목록 조회 요청", {
      companyCode,
      menuObjid,
    });

    try {
      const rules = await numberingRuleService.getRulesFromTest(
        companyCode,
        menuObjid
      );
      logger.info("[테스트] 채번 규칙 목록 조회 성공", {
        companyCode,
        menuObjid,
        count: rules.length,
      });
      return res.json({ success: true, data: rules });
    } catch (error: any) {
      logger.error("[테스트] 채번 규칙 목록 조회 실패", {
        error: error.message,
      });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// [테스트] 테이블+컬럼 기반 채번 규칙 조회
router.get(
  "/test/by-column/:tableName/:columnName",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { tableName, columnName } = req.params;

    try {
      const rule = await numberingRuleService.getNumberingRuleByColumn(
        companyCode,
        tableName,
        columnName
      );
      return res.json({ success: true, data: rule });
    } catch (error: any) {
      logger.error("테이블+컬럼 기반 채번 규칙 조회 실패", {
        error: error.message,
      });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// [테스트] 테스트 테이블에 채번 규칙 저장
// 채번 규칙은 독립적으로 생성 가능 (나중에 테이블 타입 관리에서 컬럼에 연결)
router.post(
  "/test/save",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const userId = req.user!.userId;
    const ruleConfig = req.body;

    logger.info("[테스트] 채번 규칙 저장 요청", {
      ruleId: ruleConfig.ruleId,
      ruleName: ruleConfig.ruleName,
      tableName: ruleConfig.tableName || "(미지정)",
      columnName: ruleConfig.columnName || "(미지정)",
    });

    try {
      // ruleName만 필수, tableName/columnName은 선택 (나중에 테이블 타입 관리에서 연결)
      if (!ruleConfig.ruleName) {
        return res.status(400).json({
          success: false,
          error: "ruleName is required",
        });
      }

      const savedRule = await numberingRuleService.saveRuleToTest(
        ruleConfig,
        companyCode,
        userId
      );

      const isUpdate = !!ruleConfig.ruleId;

      const resetPeriodLabel: Record<string, string> = {
        none: "초기화 안함", daily: "일별", monthly: "월별", yearly: "연별",
      };
      const partTypeLabel: Record<string, string> = {
        sequence: "순번", number: "숫자", date: "날짜", text: "문자", category: "카테고리", reference: "참조",
      };
      const partsDescription = (ruleConfig.parts || [])
        .sort((a: any, b: any) => (a.order || 0) - (b.order || 0))
        .map((p: any) => {
          const type = partTypeLabel[p.partType] || p.partType;
          if (p.partType === "text" && p.autoConfig?.textValue) return `${type}("${p.autoConfig.textValue}")`;
          if (p.partType === "sequence" && p.autoConfig?.sequenceLength) return `${type}(${p.autoConfig.sequenceLength}자리)`;
          if (p.partType === "date" && p.autoConfig?.dateFormat) return `${type}(${p.autoConfig.dateFormat})`;
          if (p.partType === "category") return `${type}(${p.autoConfig?.categoryKey || ""})`;
          if (p.partType === "reference") return `${type}(${p.autoConfig?.referenceColumnName || ""})`;
          return type;
        })
        .join(` ${ruleConfig.separator || "-"} `);

      auditLogService.log({
        companyCode,
        userId,
        userName: req.user?.userName,
        action: isUpdate ? "UPDATE" : "CREATE",
        resourceType: "NUMBERING_RULE",
        resourceId: String(savedRule.ruleId),
        resourceName: ruleConfig.ruleName,
        tableName: "numbering_rules",
        summary: isUpdate
          ? `채번 규칙 "${ruleConfig.ruleName}" 수정`
          : `채번 규칙 "${ruleConfig.ruleName}" 생성`,
        changes: {
          after: {
            규칙명: ruleConfig.ruleName,
            적용테이블: ruleConfig.tableName || "(미지정)",
            적용컬럼: ruleConfig.columnName || "(미지정)",
            구분자: ruleConfig.separator || "-",
            리셋주기: resetPeriodLabel[ruleConfig.resetPeriod] || ruleConfig.resetPeriod || "초기화 안함",
            적용범위: ruleConfig.scopeType === "menu" ? "메뉴별" : "전역",
            코드구성: partsDescription || "(파트 없음)",
            파트수: (ruleConfig.parts || []).length,
          },
        },
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.json({ success: true, data: savedRule });
    } catch (error: any) {
      logger.error("[테스트] 채번 규칙 저장 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// [테스트] 테스트 테이블에서 채번 규칙 삭제
router.delete(
  "/test/:ruleId",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const userId = req.user!.userId;
    const { ruleId } = req.params;

    try {
      await numberingRuleService.deleteRuleFromTest(ruleId, companyCode);

      auditLogService.log({
        companyCode,
        userId,
        userName: req.user?.userName,
        action: "DELETE",
        resourceType: "NUMBERING_RULE",
        resourceId: ruleId,
        tableName: "numbering_rules",
        summary: `채번 규칙(ID:${ruleId}) 삭제`,
        ipAddress: getClientIp(req),
        requestPath: req.originalUrl,
      });

      return res.json({
        success: true,
        message: "테스트 채번 규칙이 삭제되었습니다",
      });
    } catch (error: any) {
      logger.error("[테스트] 채번 규칙 삭제 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// [테스트] 코드 미리보기 (테스트 테이블 사용)
router.post(
  "/test/:ruleId/preview",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const companyCode = req.user!.companyCode;
    const { ruleId } = req.params;
    const { formData } = req.body;

    try {
      const previewCode = await numberingRuleService.previewCode(
        ruleId,
        companyCode,
        formData
      );
      return res.json({ success: true, data: { generatedCode: previewCode } });
    } catch (error: any) {
      logger.error("[테스트] 코드 미리보기 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

// ==================== 회사별 채번규칙 복제 API ====================

// 회사별 채번규칙 복제
router.post(
  "/copy-for-company",
  authenticateToken,
  async (req: AuthenticatedRequest, res: Response) => {
    const userCompanyCode = req.user!.companyCode;
    const { sourceCompanyCode, targetCompanyCode } = req.body;

    // 최고 관리자만 사용 가능
    if (userCompanyCode !== "*") {
      return res.status(403).json({
        success: false,
        error: "최고 관리자만 사용할 수 있습니다",
      });
    }

    if (!sourceCompanyCode || !targetCompanyCode) {
      return res.status(400).json({
        success: false,
        error: "sourceCompanyCode와 targetCompanyCode가 필요합니다",
      });
    }

    try {
      const result = await numberingRuleService.copyRulesForCompany(
        sourceCompanyCode,
        targetCompanyCode
      );
      return res.json({ success: true, data: result });
    } catch (error: any) {
      logger.error("회사별 채번규칙 복제 실패", { error: error.message });
      return res.status(500).json({ success: false, error: error.message });
    }
  }
);

export default router;
