import { Router, Request, Response } from "express";
import { getPool } from "../database/db";
import logger from "../utils/logger";
import { authenticateToken } from "../middleware/authMiddleware";

const router = Router();

// SQL 인젝션 방지: 테이블명/컬럼명 패턴 검증
const SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;

function isSafeIdentifier(name: string): boolean {
  return SAFE_IDENTIFIER.test(name);
}

interface MappingInfo {
  targetTable: string;
  columnMapping: Record<string, string>;
}

interface StatusConditionRule {
  whenColumn: string;
  operator: string;
  whenValue: string;
  thenValue: string;
}

interface ConditionalValueRule {
  conditions: StatusConditionRule[];
  defaultValue?: string;
}

interface StatusChangeRuleBody {
  targetTable: string;
  targetColumn: string;
  lookupMode?: "auto" | "manual";
  manualItemField?: string;
  manualPkColumn?: string;
  valueType: "fixed" | "conditional";
  fixedValue?: string;
  conditionalValue?: ConditionalValueRule;
  // 하위호환: 기존 형식
  value?: string;
  condition?: string;
}

interface ExecuteActionBody {
  action: string;
  data: {
    items?: Record<string, unknown>[];
    fieldValues?: Record<string, unknown>;
  };
  mappings?: {
    cardList?: MappingInfo | null;
    field?: MappingInfo | null;
  };
  statusChanges?: StatusChangeRuleBody[];
}

function resolveStatusValue(
  valueType: string,
  fixedValue: string,
  conditionalValue: ConditionalValueRule | undefined,
  item: Record<string, unknown>
): string {
  if (valueType !== "conditional" || !conditionalValue) return fixedValue;

  for (const cond of conditionalValue.conditions) {
    const actual = String(item[cond.whenColumn] ?? "");
    const expected = cond.whenValue;
    let match = false;

    switch (cond.operator) {
      case "=": match = actual === expected; break;
      case "!=": match = actual !== expected; break;
      case ">": match = parseFloat(actual) > parseFloat(expected); break;
      case "<": match = parseFloat(actual) < parseFloat(expected); break;
      case ">=": match = parseFloat(actual) >= parseFloat(expected); break;
      case "<=": match = parseFloat(actual) <= parseFloat(expected); break;
      default: match = actual === expected;
    }

    if (match) return cond.thenValue;
  }

  return conditionalValue.defaultValue ?? fixedValue;
}

router.post("/execute-action", authenticateToken, async (req: Request, res: Response) => {
  const pool = getPool();
  const client = await pool.connect();

  try {
    const companyCode = (req as any).user?.companyCode;
    const userId = (req as any).user?.userId;
    if (!companyCode) {
      return res.status(401).json({ success: false, message: "인증 정보가 없습니다." });
    }

    const { action, data, mappings, statusChanges } = req.body as ExecuteActionBody;
    const items = data?.items ?? [];
    const fieldValues = data?.fieldValues ?? {};

    logger.info("[pop/execute-action] 요청", {
      action,
      companyCode,
      userId,
      itemCount: items.length,
      hasFieldValues: Object.keys(fieldValues).length > 0,
      hasMappings: !!mappings,
      statusChangeCount: statusChanges?.length ?? 0,
    });

    await client.query("BEGIN");

    let processedCount = 0;
    let insertedCount = 0;

    if (action === "inbound-confirm") {
      // 1. 매핑 기반 INSERT (장바구니 데이터 -> 대상 테이블)
      const cardMapping = mappings?.cardList;
      const fieldMapping = mappings?.field;

      if (cardMapping?.targetTable && Object.keys(cardMapping.columnMapping).length > 0) {
        if (!isSafeIdentifier(cardMapping.targetTable)) {
          throw new Error(`유효하지 않은 테이블명: ${cardMapping.targetTable}`);
        }

        for (const item of items) {
          const columns: string[] = ["company_code"];
          const values: unknown[] = [companyCode];

          for (const [sourceField, targetColumn] of Object.entries(cardMapping.columnMapping)) {
            if (!isSafeIdentifier(targetColumn)) continue;
            columns.push(`"${targetColumn}"`);
            values.push(item[sourceField] ?? null);
          }

          if (fieldMapping?.targetTable === cardMapping.targetTable) {
            for (const [sourceField, targetColumn] of Object.entries(fieldMapping.columnMapping)) {
              if (!isSafeIdentifier(targetColumn)) continue;
              if (columns.includes(`"${targetColumn}"`)) continue;
              columns.push(`"${targetColumn}"`);
              values.push(fieldValues[sourceField] ?? null);
            }
          }

          if (columns.length > 1) {
            const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
            const sql = `INSERT INTO "${cardMapping.targetTable}" (${columns.join(", ")}) VALUES (${placeholders})`;

            logger.info("[pop/execute-action] INSERT 실행", {
              table: cardMapping.targetTable,
              columnCount: columns.length,
            });

            await client.query(sql, values);
            insertedCount++;
          }
        }
      }

      if (
        fieldMapping?.targetTable &&
        Object.keys(fieldMapping.columnMapping).length > 0 &&
        fieldMapping.targetTable !== cardMapping?.targetTable
      ) {
        if (!isSafeIdentifier(fieldMapping.targetTable)) {
          throw new Error(`유효하지 않은 테이블명: ${fieldMapping.targetTable}`);
        }

        const columns: string[] = ["company_code"];
        const values: unknown[] = [companyCode];

        for (const [sourceField, targetColumn] of Object.entries(fieldMapping.columnMapping)) {
          if (!isSafeIdentifier(targetColumn)) continue;
          columns.push(`"${targetColumn}"`);
          values.push(fieldValues[sourceField] ?? null);
        }

        if (columns.length > 1) {
          const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
          const sql = `INSERT INTO "${fieldMapping.targetTable}" (${columns.join(", ")}) VALUES (${placeholders})`;
          await client.query(sql, values);
        }
      }

      // 2. 상태 변경 규칙 실행 (설정 기반)
      if (statusChanges && statusChanges.length > 0) {
        for (const rule of statusChanges) {
          if (!rule.targetTable || !rule.targetColumn) continue;
          if (!isSafeIdentifier(rule.targetTable) || !isSafeIdentifier(rule.targetColumn)) {
            logger.warn("[pop/execute-action] 유효하지 않은 식별자, 건너뜀", { table: rule.targetTable, column: rule.targetColumn });
            continue;
          }

          const valueType = rule.valueType ?? "fixed";
          const fixedValue = rule.fixedValue ?? rule.value ?? "";
          const lookupMode = rule.lookupMode ?? "auto";

          // 조회 키 결정: 아이템 필드(itemField) -> 대상 테이블 PK 컬럼(pkColumn)
          let itemField: string;
          let pkColumn: string;

          if (lookupMode === "manual" && rule.manualItemField && rule.manualPkColumn) {
            if (!isSafeIdentifier(rule.manualPkColumn)) {
              logger.warn("[pop/execute-action] 수동 PK 컬럼 유효하지 않음", { manualPkColumn: rule.manualPkColumn });
              continue;
            }
            itemField = rule.manualItemField;
            pkColumn = rule.manualPkColumn;
            logger.info("[pop/execute-action] 수동 조회 키", { itemField, pkColumn, table: rule.targetTable });
          } else if (rule.targetTable === "cart_items") {
            itemField = "__cart_id";
            pkColumn = "id";
          } else {
            itemField = "__cart_row_key";
            const pkResult = await client.query(
              `SELECT a.attname FROM pg_index i JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) WHERE i.indrelid = $1::regclass AND i.indisprimary`,
              [rule.targetTable]
            );
            pkColumn = pkResult.rows[0]?.attname || "id";
          }

          const lookupValues = items.map((item) => item[itemField] ?? item[itemField.replace(/^__cart_/, "")]).filter(Boolean);
          if (lookupValues.length === 0) {
            logger.warn("[pop/execute-action] 조회 키 값 없음, 건너뜀", { table: rule.targetTable, itemField });
            continue;
          }

          if (valueType === "fixed") {
            const placeholders = lookupValues.map((_, i) => `$${i + 3}`).join(", ");
            const sql = `UPDATE "${rule.targetTable}" SET "${rule.targetColumn}" = $1 WHERE company_code = $2 AND "${pkColumn}" IN (${placeholders})`;
            await client.query(sql, [fixedValue, companyCode, ...lookupValues]);
            processedCount += lookupValues.length;
          } else {
            for (let i = 0; i < lookupValues.length; i++) {
              const item = items[i] ?? {};
              const resolvedValue = resolveStatusValue(valueType, fixedValue, rule.conditionalValue, item);
              await client.query(
                `UPDATE "${rule.targetTable}" SET "${rule.targetColumn}" = $1 WHERE company_code = $2 AND "${pkColumn}" = $3`,
                [resolvedValue, companyCode, lookupValues[i]]
              );
              processedCount++;
            }
          }

          logger.info("[pop/execute-action] 상태 변경 실행", {
            table: rule.targetTable, column: rule.targetColumn, lookupMode, itemField, pkColumn, count: lookupValues.length,
          });
        }
      }
    }

    await client.query("COMMIT");

    logger.info("[pop/execute-action] 완료", {
      action,
      companyCode,
      processedCount,
      insertedCount,
    });

    return res.json({
      success: true,
      message: `${processedCount}건 처리 완료${insertedCount > 0 ? `, ${insertedCount}건 생성` : ""}`,
      data: { processedCount, insertedCount },
    });
  } catch (error: any) {
    await client.query("ROLLBACK");
    logger.error("[pop/execute-action] 오류:", error);
    return res.status(500).json({
      success: false,
      message: error.message || "처리 중 오류가 발생했습니다.",
    });
  } finally {
    client.release();
  }
});

export default router;
