import { Request, Response } from "express";
import pool from "../database/db";
import { logger } from "../utils/logger";

interface AuthenticatedRequest extends Request {
  user?: {
    userId: string;
    userName: string;
    companyCode: string;
  };
}

/**
 * 코드 병합 - 모든 관련 테이블에 적용
 * 데이터(레코드)는 삭제하지 않고, 컬럼 값만 변경
 */
export async function mergeCodeAllTables(
  req: AuthenticatedRequest,
  res: Response
): Promise<void> {
  const { columnName, oldValue, newValue } = req.body;
  const companyCode = req.user?.companyCode;

  try {
    // 입력값 검증
    if (!columnName || !oldValue || !newValue) {
      res.status(400).json({
        success: false,
        message: "필수 필드가 누락되었습니다. (columnName, oldValue, newValue)",
      });
      return;
    }

    if (!companyCode) {
      res.status(401).json({
        success: false,
        message: "인증 정보가 없습니다.",
      });
      return;
    }

    // 같은 값으로 병합 시도 방지
    if (oldValue === newValue) {
      res.status(400).json({
        success: false,
        message: "기존 값과 새 값이 동일합니다.",
      });
      return;
    }

    logger.info("코드 병합 시작", {
      columnName,
      oldValue,
      newValue,
      companyCode,
      userId: req.user?.userId,
    });

    // PostgreSQL 함수 호출
    const result = await pool.query(
      "SELECT * FROM merge_code_all_tables($1, $2, $3, $4)",
      [columnName, oldValue, newValue, companyCode]
    );

    // 결과 처리 (pool.query 반환 타입 처리)
    const affectedTables = Array.isArray(result) ? result : ((result as any).rows || []);
    const totalRows = affectedTables.reduce(
      (sum: number, row: any) => sum + parseInt(row.rows_updated || 0),
      0
    );

    logger.info("코드 병합 완료", {
      columnName,
      oldValue,
      newValue,
      affectedTablesCount: affectedTables.length,
      totalRowsUpdated: totalRows,
    });

    res.json({
      success: true,
      message: `코드 병합 완료: ${oldValue} → ${newValue}`,
      data: {
        columnName,
        oldValue,
        newValue,
        affectedTables: affectedTables.map((row) => ({
          tableName: row.table_name,
          rowsUpdated: parseInt(row.rows_updated),
        })),
        totalRowsUpdated: totalRows,
      },
    });
  } catch (error: any) {
    logger.error("코드 병합 실패:", {
      error: error.message,
      stack: error.stack,
      columnName,
      oldValue,
      newValue,
    });

    res.status(500).json({
      success: false,
      message: "코드 병합 중 오류가 발생했습니다.",
      error: {
        code: "CODE_MERGE_ERROR",
        details: error.message,
      },
    });
  }
}

/**
 * 특정 컬럼을 가진 테이블 목록 조회
 */
export async function getTablesWithColumn(
  req: AuthenticatedRequest,
  res: Response
): Promise<void> {
  const { columnName } = req.params;

  try {
    if (!columnName) {
      res.status(400).json({
        success: false,
        message: "컬럼명이 필요합니다.",
      });
      return;
    }

    logger.info("컬럼을 가진 테이블 목록 조회", { columnName });

    const query = `
      SELECT DISTINCT t.table_name
      FROM information_schema.columns c
      JOIN information_schema.tables t 
        ON c.table_name = t.table_name
      WHERE c.column_name = $1
        AND t.table_schema = 'public'
        AND t.table_type = 'BASE TABLE'
        AND EXISTS (
          SELECT 1 FROM information_schema.columns c2
          WHERE c2.table_name = t.table_name
          AND c2.column_name = 'company_code'
        )
      ORDER BY t.table_name
    `;

    const result = await pool.query(query, [columnName]);
    const rows = (result as any).rows || [];

    logger.info(`컬럼을 가진 테이블 조회 완료: ${rows.length}개`);

    res.json({
      success: true,
      message: "테이블 목록 조회 성공",
      data: {
        columnName,
        tables: rows.map((row: any) => row.table_name),
        count: rows.length,
      },
    });
  } catch (error: any) {
    logger.error("테이블 목록 조회 실패:", error);

    res.status(500).json({
      success: false,
      message: "테이블 목록 조회 중 오류가 발생했습니다.",
      error: {
        code: "TABLE_LIST_ERROR",
        details: error.message,
      },
    });
  }
}

/**
 * 코드 병합 미리보기 (실제 실행 없이 영향받을 데이터 확인)
 */
export async function previewCodeMerge(
  req: AuthenticatedRequest,
  res: Response
): Promise<void> {
  const { columnName, oldValue } = req.body;
  const companyCode = req.user?.companyCode;

  try {
    if (!columnName || !oldValue) {
      res.status(400).json({
        success: false,
        message: "필수 필드가 누락되었습니다. (columnName, oldValue)",
      });
      return;
    }

    if (!companyCode) {
      res.status(401).json({
        success: false,
        message: "인증 정보가 없습니다.",
      });
      return;
    }

    logger.info("코드 병합 미리보기", { columnName, oldValue, companyCode });

    // 해당 컬럼을 가진 테이블 찾기
    const tablesQuery = `
      SELECT DISTINCT t.table_name
      FROM information_schema.columns c
      JOIN information_schema.tables t 
        ON c.table_name = t.table_name
      WHERE c.column_name = $1
        AND t.table_schema = 'public'
        AND t.table_type = 'BASE TABLE'
        AND EXISTS (
          SELECT 1 FROM information_schema.columns c2
          WHERE c2.table_name = t.table_name
          AND c2.column_name = 'company_code'
        )
    `;

    const tablesResult = await pool.query(tablesQuery, [columnName]);

    // 각 테이블에서 영향받을 행 수 계산
    const preview = [];
    const tableRows = Array.isArray(tablesResult) ? tablesResult : ((tablesResult as any).rows || []);
    
    for (const row of tableRows) {
      const tableName = row.table_name;
      
      // 동적 SQL 생성 (테이블명과 컬럼명은 파라미터 바인딩 불가)
      // SQL 인젝션 방지: 테이블명과 컬럼명은 information_schema에서 검증된 값
      const countQuery = `SELECT COUNT(*) as count FROM "${tableName}" WHERE "${columnName}" = $1 AND company_code = $2`;
      
      try {
        const countResult = await pool.query(countQuery, [oldValue, companyCode]);
        const rows = (countResult as any).rows || [];
        const count = rows.length > 0 ? parseInt(rows[0].count) : 0;

        if (count > 0) {
          preview.push({
            tableName,
            affectedRows: count,
          });
        }
      } catch (error: any) {
        logger.warn(`테이블 ${tableName} 조회 실패:`, error.message);
        // 테이블 접근 실패 시 건너뛰기
        continue;
      }
    }

    const totalRows = preview.reduce((sum, item) => sum + item.affectedRows, 0);

    logger.info("코드 병합 미리보기 완료", {
      tablesCount: preview.length,
      totalRows,
    });

    res.json({
      success: true,
      message: "코드 병합 미리보기 완료",
      data: {
        columnName,
        oldValue,
        preview,
        totalAffectedRows: totalRows,
      },
    });
  } catch (error: any) {
    logger.error("코드 병합 미리보기 실패:", error);

    res.status(500).json({
      success: false,
      message: "코드 병합 미리보기 중 오류가 발생했습니다.",
      error: {
        code: "PREVIEW_ERROR",
        details: error.message,
      },
    });
  }
}

/**
 * 값 기반 코드 병합 - 모든 테이블의 모든 컬럼에서 해당 값을 찾아 변경
 * 컬럼명에 상관없이 oldValue를 가진 모든 곳을 newValue로 변경
 */
export async function mergeCodeByValue(
  req: AuthenticatedRequest,
  res: Response
): Promise<void> {
  const { oldValue, newValue } = req.body;
  const companyCode = req.user?.companyCode;

  try {
    // 입력값 검증
    if (!oldValue || !newValue) {
      res.status(400).json({
        success: false,
        message: "필수 필드가 누락되었습니다. (oldValue, newValue)",
      });
      return;
    }

    if (!companyCode) {
      res.status(401).json({
        success: false,
        message: "인증 정보가 없습니다.",
      });
      return;
    }

    // 같은 값으로 병합 시도 방지
    if (oldValue === newValue) {
      res.status(400).json({
        success: false,
        message: "기존 값과 새 값이 동일합니다.",
      });
      return;
    }

    logger.info("값 기반 코드 병합 시작", {
      oldValue,
      newValue,
      companyCode,
      userId: req.user?.userId,
    });

    // PostgreSQL 함수 호출
    const result = await pool.query(
      "SELECT * FROM merge_code_by_value($1, $2, $3)",
      [oldValue, newValue, companyCode]
    );

    // 결과 처리
    const affectedData = Array.isArray(result) ? result : ((result as any).rows || []);
    const totalRows = affectedData.reduce(
      (sum: number, row: any) => sum + parseInt(row.out_rows_updated || 0),
      0
    );

    logger.info("값 기반 코드 병합 완료", {
      oldValue,
      newValue,
      affectedTablesCount: affectedData.length,
      totalRowsUpdated: totalRows,
    });

    res.json({
      success: true,
      message: `코드 병합 완료: ${oldValue} → ${newValue}`,
      data: {
        oldValue,
        newValue,
        affectedData: affectedData.map((row: any) => ({
          tableName: row.out_table_name,
          columnName: row.out_column_name,
          rowsUpdated: parseInt(row.out_rows_updated),
        })),
        totalRowsUpdated: totalRows,
      },
    });
  } catch (error: any) {
    logger.error("값 기반 코드 병합 실패:", {
      error: error.message,
      stack: error.stack,
      oldValue,
      newValue,
    });

    res.status(500).json({
      success: false,
      message: "코드 병합 중 오류가 발생했습니다.",
      error: {
        code: "CODE_MERGE_BY_VALUE_ERROR",
        details: error.message,
      },
    });
  }
}

/**
 * 값 기반 코드 병합 미리보기
 * 컬럼명에 상관없이 해당 값을 가진 모든 테이블/컬럼 조회
 */
export async function previewMergeCodeByValue(
  req: AuthenticatedRequest,
  res: Response
): Promise<void> {
  const { oldValue } = req.body;
  const companyCode = req.user?.companyCode;

  try {
    if (!oldValue) {
      res.status(400).json({
        success: false,
        message: "필수 필드가 누락되었습니다. (oldValue)",
      });
      return;
    }

    if (!companyCode) {
      res.status(401).json({
        success: false,
        message: "인증 정보가 없습니다.",
      });
      return;
    }

    logger.info("값 기반 코드 병합 미리보기", { oldValue, companyCode });

    // PostgreSQL 함수 호출
    const result = await pool.query(
      "SELECT * FROM preview_merge_code_by_value($1, $2)",
      [oldValue, companyCode]
    );

    const preview = Array.isArray(result) ? result : ((result as any).rows || []);
    const totalRows = preview.reduce(
      (sum: number, row: any) => sum + parseInt(row.out_affected_rows || 0),
      0
    );

    logger.info("값 기반 코드 병합 미리보기 완료", {
      tablesCount: preview.length,
      totalRows,
    });

    res.json({
      success: true,
      message: "코드 병합 미리보기 완료",
      data: {
        oldValue,
        preview: preview.map((row: any) => ({
          tableName: row.out_table_name,
          columnName: row.out_column_name,
          affectedRows: parseInt(row.out_affected_rows),
        })),
        totalAffectedRows: totalRows,
      },
    });
  } catch (error: any) {
    logger.error("값 기반 코드 병합 미리보기 실패:", error);

    res.status(500).json({
      success: false,
      message: "코드 병합 미리보기 중 오류가 발생했습니다.",
      error: {
        code: "PREVIEW_BY_VALUE_ERROR",
        details: error.message,
      },
    });
  }
}

