import { Response } from "express";
import https from "https";
import axios, { AxiosRequestConfig } from "axios";
import { logger } from "../utils/logger";
import { AuthenticatedRequest } from "../middleware/authMiddleware";
import { DashboardService } from "../services/DashboardService";
import {
  CreateDashboardRequest,
  UpdateDashboardRequest,
  DashboardListQuery,
} from "../types/dashboard";
import { PostgreSQLService } from "../database/PostgreSQLService";
import { ExternalRestApiConnectionService } from "../services/externalRestApiConnectionService";

/**
 * 대시보드 컨트롤러
 * - REST API 엔드포인트 처리
 * - 요청 검증 및 응답 포맷팅
 */
export class DashboardController {
  /**
   * 대시보드 생성
   * POST /api/dashboards
   */
  async createDashboard(
    req: AuthenticatedRequest,
    res: Response
  ): Promise<void> {
    try {
      const userId = req.user?.userId;
      const companyCode = req.user?.companyCode;

      if (!userId) {
        res.status(401).json({
          success: false,
          message: "인증이 필요합니다.",
        });
        return;
      }

      const {
        title,
        description,
        elements,
        isPublic = false,
        tags,
        category,
        settings,
      }: CreateDashboardRequest = req.body;

      // 유효성 검증
      if (!title || title.trim().length === 0) {
        res.status(400).json({
          success: false,
          message: "대시보드 제목이 필요합니다.",
        });
        return;
      }

      if (!elements || !Array.isArray(elements)) {
        res.status(400).json({
          success: false,
          message: "대시보드 요소 데이터가 필요합니다.",
        });
        return;
      }

      // 제목 길이 체크
      if (title.length > 200) {
        res.status(400).json({
          success: false,
          message: "제목은 200자를 초과할 수 없습니다.",
        });
        return;
      }

      // 설명 길이 체크
      if (description && description.length > 1000) {
        res.status(400).json({
          success: false,
          message: "설명은 1000자를 초과할 수 없습니다.",
        });
        return;
      }

      const dashboardData: CreateDashboardRequest = {
        title: title.trim(),
        description: description?.trim(),
        isPublic,
        elements,
        tags,
        category,
        settings,
      };

      // console.log('대시보드 생성 시작:', { title: dashboardData.title, userId, elementsCount: elements.length });

      const savedDashboard = await DashboardService.createDashboard(
        dashboardData,
        userId,
        companyCode
      );

      // console.log('대시보드 생성 성공:', { id: savedDashboard.id, title: savedDashboard.title });

      res.status(201).json({
        success: true,
        data: savedDashboard,
        message: "대시보드가 성공적으로 생성되었습니다.",
      });
    } catch (error: any) {
      // console.error('Dashboard creation error:', {
      //   message: error?.message,
      //   stack: error?.stack,
      //   error
      // });
      res.status(500).json({
        success: false,
        message: error?.message || "대시보드 생성 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development" ? error?.message : undefined,
      });
    }
  }

  /**
   * 대시보드 목록 조회
   * GET /api/dashboards
   */
  async getDashboards(req: AuthenticatedRequest, res: Response): Promise<void> {
    try {
      const userId = req.user?.userId;
      const companyCode = req.user?.companyCode;

      const query: DashboardListQuery = {
        page: parseInt(req.query.page as string) || 1,
        limit: Math.min(parseInt(req.query.limit as string) || 20, 100), // 최대 100개
        search: req.query.search as string,
        category: req.query.category as string,
        isPublic:
          req.query.isPublic === "true"
            ? true
            : req.query.isPublic === "false"
              ? false
              : undefined,
        createdBy: req.query.createdBy as string,
      };

      // 페이지 번호 유효성 검증
      if (query.page! < 1) {
        res.status(400).json({
          success: false,
          message: "페이지 번호는 1 이상이어야 합니다.",
        });
        return;
      }

      const result = await DashboardService.getDashboards(
        query,
        userId,
        companyCode
      );

      res.json({
        success: true,
        data: result.dashboards,
        pagination: result.pagination,
      });
    } catch (error) {
      // console.error('Dashboard list error:', error);
      res.status(500).json({
        success: false,
        message: "대시보드 목록 조회 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? (error as Error).message
            : undefined,
      });
    }
  }

  /**
   * 대시보드 상세 조회
   * GET /api/dashboards/:id
   */
  async getDashboard(req: AuthenticatedRequest, res: Response): Promise<void> {
    try {
      const { id } = req.params;
      const userId = req.user?.userId;
      const companyCode = req.user?.companyCode;

      if (!id) {
        res.status(400).json({
          success: false,
          message: "대시보드 ID가 필요합니다.",
        });
        return;
      }

      const dashboard = await DashboardService.getDashboardById(
        id,
        userId,
        companyCode
      );

      if (!dashboard) {
        res.status(404).json({
          success: false,
          message: "대시보드를 찾을 수 없거나 접근 권한이 없습니다.",
        });
        return;
      }

      // 조회수 증가 (본인이 만든 대시보드가 아닌 경우에만)
      if (userId && dashboard.createdBy !== userId) {
        await DashboardService.incrementViewCount(id);
      }

      res.json({
        success: true,
        data: dashboard,
      });
    } catch (error) {
      // console.error('Dashboard get error:', error);
      res.status(500).json({
        success: false,
        message: "대시보드 조회 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? (error as Error).message
            : undefined,
      });
    }
  }

  /**
   * 대시보드 수정
   * PUT /api/dashboards/:id
   */
  async updateDashboard(
    req: AuthenticatedRequest,
    res: Response
  ): Promise<void> {
    try {
      const { id } = req.params;
      const userId = req.user?.userId;

      if (!userId) {
        res.status(401).json({
          success: false,
          message: "인증이 필요합니다.",
        });
        return;
      }

      if (!id) {
        res.status(400).json({
          success: false,
          message: "대시보드 ID가 필요합니다.",
        });
        return;
      }

      const updateData: UpdateDashboardRequest = req.body;

      // 유효성 검증
      if (updateData.title !== undefined) {
        if (
          typeof updateData.title !== "string" ||
          updateData.title.trim().length === 0
        ) {
          res.status(400).json({
            success: false,
            message: "올바른 제목을 입력해주세요.",
          });
          return;
        }
        if (updateData.title.length > 200) {
          res.status(400).json({
            success: false,
            message: "제목은 200자를 초과할 수 없습니다.",
          });
          return;
        }
        updateData.title = updateData.title.trim();
      }

      if (
        updateData.description !== undefined &&
        updateData.description &&
        updateData.description.length > 1000
      ) {
        res.status(400).json({
          success: false,
          message: "설명은 1000자를 초과할 수 없습니다.",
        });
        return;
      }

      const updatedDashboard = await DashboardService.updateDashboard(
        id,
        updateData,
        userId
      );

      if (!updatedDashboard) {
        res.status(404).json({
          success: false,
          message: "대시보드를 찾을 수 없거나 수정 권한이 없습니다.",
        });
        return;
      }

      res.json({
        success: true,
        data: updatedDashboard,
        message: "대시보드가 성공적으로 수정되었습니다.",
      });
    } catch (error) {
      // console.error('Dashboard update error:', error);

      if ((error as Error).message.includes("권한이 없습니다")) {
        res.status(403).json({
          success: false,
          message: (error as Error).message,
        });
        return;
      }

      res.status(500).json({
        success: false,
        message: "대시보드 수정 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? (error as Error).message
            : undefined,
      });
    }
  }

  /**
   * 대시보드 삭제
   * DELETE /api/dashboards/:id
   */
  async deleteDashboard(
    req: AuthenticatedRequest,
    res: Response
  ): Promise<void> {
    try {
      const { id } = req.params;
      const userId = req.user?.userId;

      if (!userId) {
        res.status(401).json({
          success: false,
          message: "인증이 필요합니다.",
        });
        return;
      }

      if (!id) {
        res.status(400).json({
          success: false,
          message: "대시보드 ID가 필요합니다.",
        });
        return;
      }

      const deleted = await DashboardService.deleteDashboard(id, userId);

      if (!deleted) {
        res.status(404).json({
          success: false,
          message: "대시보드를 찾을 수 없거나 삭제 권한이 없습니다.",
        });
        return;
      }

      res.json({
        success: true,
        message: "대시보드가 성공적으로 삭제되었습니다.",
      });
    } catch (error) {
      // console.error('Dashboard delete error:', error);
      res.status(500).json({
        success: false,
        message: "대시보드 삭제 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? (error as Error).message
            : undefined,
      });
    }
  }

  /**
   * 내 대시보드 목록 조회
   * GET /api/dashboards/my
   */
  async getMyDashboards(
    req: AuthenticatedRequest,
    res: Response
  ): Promise<void> {
    try {
      const userId = req.user?.userId;

      if (!userId) {
        res.status(401).json({
          success: false,
          message: "인증이 필요합니다.",
        });
        return;
      }

      const companyCode = req.user?.companyCode;

      const query: DashboardListQuery = {
        page: parseInt(req.query.page as string) || 1,
        limit: Math.min(parseInt(req.query.limit as string) || 20, 100),
        search: req.query.search as string,
        category: req.query.category as string,
        // createdBy 제거 - 회사 대시보드 전체 표시
      };

      const result = await DashboardService.getDashboards(
        query,
        userId,
        companyCode
      );

      res.json({
        success: true,
        data: result.dashboards,
        pagination: result.pagination,
      });
    } catch (error) {
      // console.error('My dashboards error:', error);
      res.status(500).json({
        success: false,
        message: "내 대시보드 목록 조회 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? (error as Error).message
            : undefined,
      });
    }
  }

  /**
   * 쿼리 실행 (SELECT만)
   * POST /api/dashboards/execute-query
   */
  async executeQuery(req: AuthenticatedRequest, res: Response): Promise<void> {
    try {
      // 개발용으로 인증 체크 제거
      // const userId = req.user?.userId;
      // if (!userId) {
      //   res.status(401).json({
      //     success: false,
      //     message: '인증이 필요합니다.'
      //   });
      //   return;
      // }

      const { query } = req.body;

      // 유효성 검증
      if (!query || typeof query !== "string" || query.trim().length === 0) {
        res.status(400).json({
          success: false,
          message: "쿼리가 필요합니다.",
        });
        return;
      }

      // SQL 인젝션 방지를 위한 기본적인 검증
      const trimmedQuery = query.trim().toLowerCase();
      if (!trimmedQuery.startsWith("select")) {
        res.status(400).json({
          success: false,
          message: "SELECT 쿼리만 허용됩니다.",
        });
        return;
      }

      // 쿼리 실행
      const result = await PostgreSQLService.query(query.trim());

      // 결과 변환
      const columns = result.fields?.map((field) => field.name) || [];
      const rows = result.rows || [];

      res.status(200).json({
        success: true,
        data: {
          columns,
          rows,
          rowCount: rows.length,
        },
        message: "쿼리가 성공적으로 실행되었습니다.",
      });
    } catch (error) {
      // console.error('Query execution error:', error);
      res.status(500).json({
        success: false,
        message: "쿼리 실행 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? (error as Error).message
            : "쿼리 실행 오류",
      });
    }
  }

  /**
   * DML 쿼리 실행 (INSERT, UPDATE, DELETE)
   * POST /api/dashboards/execute-dml
   */
  async executeDML(req: AuthenticatedRequest, res: Response): Promise<void> {
    try {
      const { query } = req.body;

      // 유효성 검증
      if (!query || typeof query !== "string" || query.trim().length === 0) {
        res.status(400).json({
          success: false,
          message: "쿼리가 필요합니다.",
        });
        return;
      }

      // SQL 인젝션 방지를 위한 기본적인 검증
      const trimmedQuery = query.trim().toLowerCase();
      const allowedCommands = ["insert", "update", "delete"];
      const isAllowed = allowedCommands.some((cmd) =>
        trimmedQuery.startsWith(cmd)
      );

      if (!isAllowed) {
        res.status(400).json({
          success: false,
          message: "INSERT, UPDATE, DELETE 쿼리만 허용됩니다.",
        });
        return;
      }

      // 위험한 명령어 차단
      const dangerousPatterns = [
        /drop\s+table/i,
        /drop\s+database/i,
        /truncate/i,
        /alter\s+table/i,
        /create\s+table/i,
      ];

      if (dangerousPatterns.some((pattern) => pattern.test(query))) {
        res.status(403).json({
          success: false,
          message: "허용되지 않는 쿼리입니다.",
        });
        return;
      }

      // 쿼리 실행
      const result = await PostgreSQLService.query(query.trim());

      res.status(200).json({
        success: true,
        data: {
          rowCount: result.rowCount || 0,
          command: result.command,
        },
        message: "쿼리가 성공적으로 실행되었습니다.",
      });
    } catch (error) {
      console.error("DML execution error:", error);
      res.status(500).json({
        success: false,
        message: "쿼리 실행 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? (error as Error).message
            : "쿼리 실행 오류",
      });
    }
  }

  /**
   * 외부 API 프록시 (CORS 우회용)
   * POST /api/dashboards/fetch-external-api
   */
  async fetchExternalApi(
    req: AuthenticatedRequest,
    res: Response
  ): Promise<void> {
    try {
      const {
        url,
        method = "GET",
        headers = {},
        queryParams = {},
        body,
        externalConnectionId, // 프론트엔드에서 선택된 커넥션 ID를 전달받아야 함
      } = req.body;

      if (!url || typeof url !== "string") {
        res.status(400).json({
          success: false,
          message: "URL이 필요합니다.",
        });
        return;
      }

      // 쿼리 파라미터 추가
      const urlObj = new URL(url);
      Object.entries(queryParams).forEach(([key, value]) => {
        if (key && value) {
          urlObj.searchParams.append(key, String(value));
        }
      });

      // Axios 요청 설정
      const requestConfig: AxiosRequestConfig = {
        url: urlObj.toString(),
        method: method.toUpperCase(),
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
          ...headers,
        },
        timeout: 60000, // 60초 타임아웃
        validateStatus: () => true, // 모든 상태 코드 허용 (에러도 응답으로 처리)
      };

      // 연결 정보 (응답에 포함용)
      let connectionInfo: { saveToHistory?: boolean } | null = null;

      // 외부 커넥션 ID가 있는 경우, 해당 커넥션의 인증 정보(DB 토큰 등)를 적용
      if (externalConnectionId) {
        try {
          // 사용자 회사 코드가 있으면 사용하고, 없으면 '*' (최고 관리자)로 시도
          let companyCode = req.user?.companyCode;

          if (!companyCode) {
            companyCode = "*";
          }

          // 커넥션 로드
          const connectionResult =
            await ExternalRestApiConnectionService.getConnectionById(
              Number(externalConnectionId),
              companyCode
            );

          if (connectionResult.success && connectionResult.data) {
            const connection = connectionResult.data;

            // 연결 정보 저장 (응답에 포함)
            connectionInfo = {
              saveToHistory: connection.save_to_history === "Y",
            };

            // 인증 헤더 생성 (DB 토큰 등)
            const authHeaders =
              await ExternalRestApiConnectionService.getAuthHeaders(
                connection.auth_type,
                connection.auth_config,
                connection.company_code
              );

            // 기존 헤더에 인증 헤더 병합
            requestConfig.headers = {
              ...requestConfig.headers,
              ...authHeaders,
            };

            // API Key가 Query Param인 경우 처리
            if (
              connection.auth_type === "api-key" &&
              connection.auth_config?.keyLocation === "query" &&
              connection.auth_config?.keyName &&
              connection.auth_config?.keyValue
            ) {
              const currentUrl = new URL(requestConfig.url!);
              currentUrl.searchParams.append(
                connection.auth_config.keyName,
                connection.auth_config.keyValue
              );
              requestConfig.url = currentUrl.toString();
            }
          }
        } catch (connError) {
          logger.error(
            `외부 커넥션(${externalConnectionId}) 정보 로드 및 인증 적용 실패:`,
            connError
          );
        }
      }

      // Body 처리
      if (body) {
        requestConfig.data = body;
      }

      // 디버깅 로그: 실제 요청 정보 출력
      logger.info(`[fetchExternalApi] 요청 정보:`, {
        url: requestConfig.url,
        method: requestConfig.method,
        headers: requestConfig.headers,
        body: requestConfig.data,
        externalConnectionId,
      });

      // TLS 인증서 검증 예외 처리 (thiratis.com 등 내부망/레거시 API 대응)
      // ExternalRestApiConnectionService와 동일한 로직 적용
      const bypassDomains = ["thiratis.com"];
      const hostname = urlObj.hostname;
      const shouldBypassTls = bypassDomains.some((domain) =>
        hostname.includes(domain)
      );

      if (shouldBypassTls) {
        requestConfig.httpsAgent = new https.Agent({
          rejectUnauthorized: false,
        });
      }

      // 기상청 API 등 EUC-KR 인코딩을 사용하는 경우 arraybuffer로 받아서 디코딩
      const isKmaApi = urlObj.hostname.includes("kma.go.kr");
      if (isKmaApi) {
        requestConfig.responseType = "arraybuffer";
      }

      const response = await axios(requestConfig);

      if (response.status >= 400) {
        throw new Error(
          `외부 API 오류: ${response.status} ${response.statusText}`
        );
      }

      let data = response.data;
      const contentType = response.headers["content-type"];

      // 기상청 API 인코딩 처리 (UTF-8 우선, 실패 시 EUC-KR)
      if (isKmaApi && Buffer.isBuffer(data)) {
        const iconv = require("iconv-lite");
        const buffer = Buffer.from(data);
        const utf8Text = buffer.toString("utf-8");

        // UTF-8로 정상 디코딩되었는지 확인
        if (
          utf8Text.includes("특보") ||
          utf8Text.includes("경보") ||
          utf8Text.includes("주의보") ||
          (utf8Text.includes("#START7777") && !utf8Text.includes("�"))
        ) {
          data = { text: utf8Text, contentType, encoding: "utf-8" };
        } else {
          // EUC-KR로 디코딩
          const eucKrText = iconv.decode(buffer, "EUC-KR");
          data = { text: eucKrText, contentType, encoding: "euc-kr" };
        }
      }
      // 텍스트 응답인 경우 포맷팅
      else if (typeof data === "string") {
        data = { text: data, contentType };
      }

      res.status(200).json({
        success: true,
        data,
        connectionInfo, // 외부 연결 정보 (saveToHistory 등)
      });
    } catch (error: any) {
      const status = error.response?.status || 500;
      const message = error.response?.statusText || error.message;

      logger.error("외부 API 호출 오류:", {
        message,
        status,
        data: error.response?.data,
      });

      res.status(500).json({
        success: false,
        message: "외부 API 호출 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? message
            : "외부 API 호출 오류",
      });
    }
  }

  /**
   * 테이블 스키마 조회 (날짜 컬럼 감지용)
   * POST /api/dashboards/table-schema
   */
  async getTableSchema(
    req: AuthenticatedRequest,
    res: Response
  ): Promise<void> {
    try {
      const { tableName } = req.body;

      if (!tableName || typeof tableName !== "string") {
        res.status(400).json({
          success: false,
          message: "테이블명이 필요합니다.",
        });
        return;
      }

      // 테이블명 검증 (SQL 인젝션 방지)
      if (!/^[a-z_][a-z0-9_]*$/i.test(tableName)) {
        res.status(400).json({
          success: false,
          message: "유효하지 않은 테이블명입니다.",
        });
        return;
      }

      // PostgreSQL information_schema에서 컬럼 정보 조회
      const query = `
        SELECT 
          column_name,
          data_type,
          udt_name
        FROM information_schema.columns
        WHERE table_name = $1
        ORDER BY ordinal_position
      `;

      const result = await PostgreSQLService.query(query, [
        tableName.toLowerCase(),
      ]);

      // 날짜/시간 타입 컬럼 필터링
      const dateColumns = result.rows
        .filter((row: any) => {
          const dataType = row.data_type?.toLowerCase();
          const udtName = row.udt_name?.toLowerCase();
          return (
            dataType === "timestamp" ||
            dataType === "timestamp without time zone" ||
            dataType === "timestamp with time zone" ||
            dataType === "date" ||
            dataType === "time" ||
            dataType === "time without time zone" ||
            dataType === "time with time zone" ||
            udtName === "timestamp" ||
            udtName === "timestamptz" ||
            udtName === "date" ||
            udtName === "time" ||
            udtName === "timetz"
          );
        })
        .map((row: any) => row.column_name);

      res.status(200).json({
        success: true,
        data: {
          tableName,
          columns: result.rows.map((row: any) => ({
            name: row.column_name,
            type: row.data_type,
            udtName: row.udt_name,
          })),
          dateColumns,
        },
      });
    } catch (error) {
      res.status(500).json({
        success: false,
        message: "테이블 스키마 조회 중 오류가 발생했습니다.",
        error:
          process.env.NODE_ENV === "development"
            ? (error as Error).message
            : "스키마 조회 오류",
      });
    }
  }
}
