/**
 * 개선된 동적 폼 서비스
 * 타입 안전성과 검증 강화
 */

import { query, queryOne } from "../database/db";
import {
  WebType,
  DynamicWebType,
  normalizeWebType,
  isValidWebType,
  WEB_TYPE_TO_POSTGRES_CONVERTER,
  WEB_TYPE_VALIDATION_PATTERNS,
} from "../types/v2-web-types";
import { DataflowControlService } from "./dataflowControlService";

// 테이블 컬럼 정보
export interface TableColumn {
  column_name: string;
  data_type: string;
  is_nullable: string;
  column_default: any;
  character_maximum_length?: number;
  numeric_precision?: number;
  numeric_scale?: number;
}

// 컬럼 웹타입 정보
export interface ColumnWebTypeInfo {
  columnName: string;
  webType: WebType;
  isRequired: boolean;
  validationRules?: Record<string, any>;
  defaultValue?: any;
}

// 폼 데이터 검증 결과
export interface FormValidationResult {
  isValid: boolean;
  errors: FormValidationError[];
  warnings: FormValidationWarning[];
  transformedData: Record<string, any>;
}

export interface FormValidationError {
  field: string;
  code: string;
  message: string;
  value?: any;
}

export interface FormValidationWarning {
  field: string;
  code: string;
  message: string;
  suggestion?: string;
}

// 저장 결과
export interface FormDataResult {
  success: boolean;
  message: string;
  data?: any;
  affectedRows?: number;
  insertedId?: any;
  validationResult?: FormValidationResult;
}

export class EnhancedDynamicFormService {
  private dataflowControlService = new DataflowControlService();
  private columnCache = new Map<string, TableColumn[]>();
  private webTypeCache = new Map<string, ColumnWebTypeInfo[]>();

  /**
   * 폼 데이터 저장 (메인 메서드)
   */
  async saveFormData(
    screenId: number,
    tableName: string,
    data: Record<string, any>
  ): Promise<FormDataResult> {
    const startTime = Date.now();

    try {
      console.log(`🚀 개선된 폼 저장 시작: ${tableName}`, {
        screenId,
        dataKeys: Object.keys(data),
        timestamp: new Date().toISOString(),
      });

      // 1. 테이블 존재 여부 확인
      const tableExists = await this.validateTableExists(tableName);
      if (!tableExists) {
        return {
          success: false,
          message: `테이블 '${tableName}'이 존재하지 않습니다.`,
        };
      }

      // 2. 스키마 정보 로드
      const [tableColumns, columnWebTypes] = await Promise.all([
        this.getTableColumns(tableName),
        this.getColumnWebTypes(tableName),
      ]);

      // 3. 폼 데이터 검증
      const validationResult = await this.validateFormData(
        data,
        tableColumns,
        columnWebTypes,
        tableName
      );

      if (!validationResult.isValid) {
        console.error("❌ 폼 데이터 검증 실패:", validationResult.errors);
        return {
          success: false,
          message: this.formatValidationErrors(validationResult.errors),
          validationResult,
        };
      }

      // 4. 데이터 저장 수행
      const saveResult = await this.performDataSave(
        tableName,
        validationResult.transformedData,
        tableColumns
      );

      const duration = Date.now() - startTime;
      console.log(`✅ 폼 저장 완료: ${duration}ms`);

      return {
        success: true,
        message: "데이터가 성공적으로 저장되었습니다.",
        data: saveResult.data,
        affectedRows: saveResult.affectedRows,
        insertedId: saveResult.insertedId,
        validationResult,
      };
    } catch (error: any) {
      console.error("❌ 폼 저장 중 오류:", error);

      return {
        success: false,
        message: this.formatErrorMessage(error),
        data: { error: error.message, stack: error.stack },
      };
    }
  }

  /**
   * 테이블 존재 여부 확인
   */
  private async validateTableExists(tableName: string): Promise<boolean> {
    try {
      const result = await query<{ exists: boolean }>(
        `SELECT EXISTS (
          SELECT FROM information_schema.tables 
          WHERE table_name = $1
        ) as exists`,
        [tableName]
      );

      return result[0]?.exists || false;
    } catch (error) {
      console.error(`❌ 테이블 존재 여부 확인 실패: ${tableName}`, error);
      return false;
    }
  }

  /**
   * 테이블 컬럼 정보 조회 (캐시 포함)
   */
  private async getTableColumns(tableName: string): Promise<TableColumn[]> {
    // 캐시 확인
    const cached = this.columnCache.get(tableName);
    if (cached) {
      return cached;
    }

    try {
      const columns = await query<TableColumn>(
        `SELECT 
          column_name,
          data_type,
          is_nullable,
          column_default,
          character_maximum_length,
          numeric_precision,
          numeric_scale
        FROM information_schema.columns 
        WHERE table_name = $1
        ORDER BY ordinal_position`,
        [tableName]
      );

      // 캐시 저장 (10분)
      this.columnCache.set(tableName, columns);
      setTimeout(() => this.columnCache.delete(tableName), 10 * 60 * 1000);

      return columns;
    } catch (error) {
      console.error(`❌ 테이블 컬럼 정보 조회 실패: ${tableName}`, error);
      throw new Error(`테이블 컬럼 정보를 조회할 수 없습니다: ${error}`);
    }
  }

  /**
   * 컬럼 웹타입 정보 조회
   */
  private async getColumnWebTypes(
    tableName: string
  ): Promise<ColumnWebTypeInfo[]> {
    // 캐시 확인
    const cached = this.webTypeCache.get(tableName);
    if (cached) {
      return cached;
    }

    try {
      // table_type_columns에서 웹타입 정보 조회
      const webTypeData = await query<{
        column_name: string;
        web_type: string;
        is_nullable: string;
        detail_settings: any;
      }>(
        `SELECT 
          column_name,
          web_type,
          is_nullable,
          detail_settings
        FROM table_type_columns 
        WHERE table_name = $1`,
        [tableName]
      );

      const columnWebTypes: ColumnWebTypeInfo[] = webTypeData.map((row) => ({
        columnName: row.column_name,
        webType: normalizeWebType(row.web_type || "text"),
        isRequired: row.is_nullable === "N",
        validationRules: this.parseDetailSettings(row.detail_settings),
        defaultValue: null,
      }));

      // 캐시 저장 (10분)
      this.webTypeCache.set(tableName, columnWebTypes);
      setTimeout(() => this.webTypeCache.delete(tableName), 10 * 60 * 1000);

      return columnWebTypes;
    } catch (error) {
      console.error(`❌ 컬럼 웹타입 정보 조회 실패: ${tableName}`, error);
      // 실패 시 빈 배열 반환 (기본 검증만 수행)
      return [];
    }
  }

  /**
   * 상세 설정 파싱
   */
  private parseDetailSettings(
    detailSettings: string | null
  ): Record<string, any> {
    if (!detailSettings) return {};

    try {
      return JSON.parse(detailSettings);
    } catch {
      return {};
    }
  }

  /**
   * 폼 데이터 검증
   */
  private async validateFormData(
    data: Record<string, any>,
    tableColumns: TableColumn[],
    columnWebTypes: ColumnWebTypeInfo[],
    tableName: string
  ): Promise<FormValidationResult> {
    const errors: FormValidationError[] = [];
    const warnings: FormValidationWarning[] = [];
    const transformedData: Record<string, any> = {};

    const columnMap = new Map(
      tableColumns.map((col) => [col.column_name, col])
    );
    const webTypeMap = new Map(columnWebTypes.map((wt) => [wt.columnName, wt]));

    console.log(`📋 폼 데이터 검증 시작: ${tableName}`, {
      inputFields: Object.keys(data).length,
      tableColumns: tableColumns.length,
      webTypeColumns: columnWebTypes.length,
    });

    // 입력된 각 필드 검증
    for (const [fieldName, value] of Object.entries(data)) {
      const column = columnMap.get(fieldName);
      const webTypeInfo = webTypeMap.get(fieldName);

      // 1. 컬럼 존재 여부 확인
      if (!column) {
        errors.push({
          field: fieldName,
          code: "COLUMN_NOT_EXISTS",
          message: `테이블 '${tableName}'에 '${fieldName}' 컬럼이 존재하지 않습니다.`,
          value,
        });
        continue;
      }

      // 2. 필수값 검증
      if (webTypeInfo?.isRequired && this.isEmptyValue(value)) {
        errors.push({
          field: fieldName,
          code: "REQUIRED_FIELD",
          message: `'${fieldName}'은(는) 필수 입력 항목입니다.`,
          value,
        });
        continue;
      }

      // 3. 웹타입별 검증 및 변환
      if (webTypeInfo?.webType) {
        const validationResult = this.validateFieldByWebType(
          fieldName,
          value,
          webTypeInfo.webType,
          webTypeInfo.validationRules
        );

        if (!validationResult.isValid) {
          errors.push(validationResult.error!);
          continue;
        }

        transformedData[fieldName] = validationResult.transformedValue;
      } else {
        // 웹타입 정보가 없는 경우 DB 타입 기반 변환
        transformedData[fieldName] = this.convertValueForPostgreSQL(
          value,
          column.data_type
        );
      }

      // 4. DB 제약조건 검증
      const constraintValidation = this.validateDatabaseConstraints(
        fieldName,
        transformedData[fieldName],
        column
      );

      if (!constraintValidation.isValid) {
        errors.push(constraintValidation.error!);
        continue;
      }
    }

    // 필수 컬럼 누락 확인
    const requiredColumns = columnWebTypes.filter((wt) => wt.isRequired);
    for (const requiredCol of requiredColumns) {
      if (
        !(requiredCol.columnName in data) ||
        this.isEmptyValue(data[requiredCol.columnName])
      ) {
        if (!errors.some((e) => e.field === requiredCol.columnName)) {
          errors.push({
            field: requiredCol.columnName,
            code: "MISSING_REQUIRED_FIELD",
            message: `필수 입력 항목 '${requiredCol.columnName}'이 누락되었습니다.`,
          });
        }
      }
    }

    console.log(`📋 폼 데이터 검증 완료:`, {
      errors: errors.length,
      warnings: warnings.length,
      transformedFields: Object.keys(transformedData).length,
    });

    return {
      isValid: errors.length === 0,
      errors,
      warnings,
      transformedData,
    };
  }

  /**
   * 웹타입별 필드 검증
   */
  private validateFieldByWebType(
    fieldName: string,
    value: any,
    webType: WebType,
    validationRules?: Record<string, any>
  ): { isValid: boolean; error?: FormValidationError; transformedValue?: any } {
    // 빈 값 처리
    if (this.isEmptyValue(value)) {
      return { isValid: true, transformedValue: null };
    }

    // 웹타입 유효성 확인
    if (!isValidWebType(webType)) {
      return {
        isValid: false,
        error: {
          field: fieldName,
          code: "INVALID_WEB_TYPE",
          message: `'${fieldName}'의 웹타입 '${webType}'이 올바르지 않습니다.`,
          value,
        },
      };
    }

    // 패턴 검증
    const pattern = WEB_TYPE_VALIDATION_PATTERNS[webType];
    if (pattern && !pattern.test(String(value))) {
      return {
        isValid: false,
        error: {
          field: fieldName,
          code: "INVALID_FORMAT",
          message: `'${fieldName}'의 형식이 올바르지 않습니다.`,
          value,
        },
      };
    }

    // 값 변환
    try {
      const converter = WEB_TYPE_TO_POSTGRES_CONVERTER[webType];
      const transformedValue = converter ? converter(value) : value;

      return { isValid: true, transformedValue };
    } catch (error) {
      return {
        isValid: false,
        error: {
          field: fieldName,
          code: "CONVERSION_ERROR",
          message: `'${fieldName}' 값 변환 중 오류가 발생했습니다: ${error}`,
          value,
        },
      };
    }
  }

  /**
   * 데이터베이스 제약조건 검증
   */
  private validateDatabaseConstraints(
    fieldName: string,
    value: any,
    column: TableColumn
  ): { isValid: boolean; error?: FormValidationError } {
    // NULL 제약조건
    if (
      column.is_nullable === "NO" &&
      (value === null || value === undefined)
    ) {
      return {
        isValid: false,
        error: {
          field: fieldName,
          code: "NOT_NULL_VIOLATION",
          message: `'${fieldName}'에는 NULL 값을 입력할 수 없습니다.`,
          value,
        },
      };
    }

    // 문자열 길이 제약조건
    if (column.character_maximum_length && typeof value === "string") {
      if (value.length > column.character_maximum_length) {
        return {
          isValid: false,
          error: {
            field: fieldName,
            code: "STRING_TOO_LONG",
            message: `'${fieldName}'의 길이는 최대 ${column.character_maximum_length}자까지 입력할 수 있습니다.`,
            value,
          },
        };
      }
    }

    // 숫자 정밀도 검증
    if (column.numeric_precision && typeof value === "number") {
      const totalDigits = Math.abs(value).toString().replace(".", "").length;
      if (totalDigits > column.numeric_precision) {
        return {
          isValid: false,
          error: {
            field: fieldName,
            code: "NUMERIC_OVERFLOW",
            message: `'${fieldName}'의 숫자 자릿수가 허용 범위를 초과했습니다.`,
            value,
          },
        };
      }
    }

    return { isValid: true };
  }

  /**
   * 빈 값 확인
   */
  private isEmptyValue(value: any): boolean {
    return (
      value === null ||
      value === undefined ||
      value === "" ||
      (Array.isArray(value) && value.length === 0)
    );
  }

  /**
   * 데이터 저장 수행
   */
  private async performDataSave(
    tableName: string,
    data: Record<string, any>,
    tableColumns: TableColumn[]
  ): Promise<{ data?: any; affectedRows: number; insertedId?: any }> {
    try {
      // Primary Key 확인
      const primaryKeys = await this.getPrimaryKeys(tableName);
      const hasExistingRecord =
        primaryKeys.length > 0 &&
        primaryKeys.every((pk) => data[pk] !== undefined && data[pk] !== null);

      if (hasExistingRecord) {
        // UPDATE 수행
        return await this.performUpdate(tableName, data, primaryKeys);
      } else {
        // INSERT 수행
        return await this.performInsert(tableName, data);
      }
    } catch (error) {
      console.error(`❌ 데이터 저장 실패: ${tableName}`, error);
      throw error;
    }
  }

  /**
   * Primary Key 조회
   */
  private async getPrimaryKeys(tableName: string): Promise<string[]> {
    try {
      const result = await query<{ column_name: string }>(
        `SELECT column_name
        FROM information_schema.key_column_usage
        WHERE table_name = $1 
        AND constraint_name LIKE '%_pkey'`,
        [tableName]
      );

      return result.map((row) => row.column_name);
    } catch (error) {
      console.error(`❌ Primary Key 조회 실패: ${tableName}`, error);
      return [];
    }
  }

  /**
   * INSERT 수행
   */
  private async performInsert(
    tableName: string,
    data: Record<string, any>
  ): Promise<{ data?: any; affectedRows: number; insertedId?: any }> {
    const columns = Object.keys(data);
    const values = Object.values(data);
    const placeholders = values.map((_, index) => `$${index + 1}`).join(", ");

    const insertQuery = `
      INSERT INTO ${tableName} (${columns.join(", ")})
      VALUES (${placeholders})
      RETURNING *
    `;

    console.log(`📝 INSERT 쿼리 실행: ${tableName}`, {
      columns: columns.length,
      query: insertQuery.replace(/\n\s+/g, " "),
    });

    const result = await query<any>(insertQuery, values);

    return {
      data: result[0],
      affectedRows: result.length,
      insertedId: result[0]?.id || result[0],
    };
  }

  /**
   * UPDATE 수행
   */
  private async performUpdate(
    tableName: string,
    data: Record<string, any>,
    primaryKeys: string[]
  ): Promise<{ data?: any; affectedRows: number; insertedId?: any }> {
    const updateColumns = Object.keys(data).filter(
      (col) => !primaryKeys.includes(col)
    );
    const whereColumns = primaryKeys.filter((pk) => data[pk] !== undefined);

    if (updateColumns.length === 0) {
      throw new Error("업데이트할 컬럼이 없습니다.");
    }

    const setClause = updateColumns
      .map((col, index) => `${col} = $${index + 1}`)
      .join(", ");

    const whereClause = whereColumns
      .map((col, index) => `${col} = $${updateColumns.length + index + 1}`)
      .join(" AND ");

    const updateValues = [
      ...updateColumns.map((col) => data[col]),
      ...whereColumns.map((col) => data[col]),
    ];

    const updateQuery = `
      UPDATE ${tableName} 
      SET ${setClause}
      WHERE ${whereClause}
      RETURNING *
    `;

    console.log(`📝 UPDATE 쿼리 실행: ${tableName}`, {
      updateColumns: updateColumns.length,
      whereColumns: whereColumns.length,
      query: updateQuery.replace(/\n\s+/g, " "),
    });

    const result = await query<any>(updateQuery, updateValues);

    return {
      data: result[0],
      affectedRows: result.length,
    };
  }

  /**
   * PostgreSQL 타입 변환 (레거시 지원)
   */
  private convertValueForPostgreSQL(value: any, dataType: string): any {
    if (value === null || value === undefined || value === "") {
      return null;
    }

    const lowerDataType = dataType.toLowerCase();

    // 숫자 타입 처리
    if (
      lowerDataType.includes("integer") ||
      lowerDataType.includes("bigint") ||
      lowerDataType.includes("serial")
    ) {
      return parseInt(value) || null;
    }

    if (
      lowerDataType.includes("numeric") ||
      lowerDataType.includes("decimal") ||
      lowerDataType.includes("real") ||
      lowerDataType.includes("double")
    ) {
      return parseFloat(value) || null;
    }

    // 불린 타입 처리
    if (lowerDataType.includes("boolean")) {
      if (typeof value === "boolean") return value;
      if (typeof value === "string") {
        return value.toLowerCase() === "true" || value === "1";
      }
      return Boolean(value);
    }

    // 날짜/시간 타입 처리
    if (
      lowerDataType.includes("timestamp") ||
      lowerDataType.includes("datetime")
    ) {
      const date = new Date(value);
      return isNaN(date.getTime()) ? null : date.toISOString();
    }

    if (lowerDataType.includes("date")) {
      const date = new Date(value);
      return isNaN(date.getTime()) ? null : date.toISOString().split("T")[0];
    }

    // JSON 타입 처리
    if (lowerDataType.includes("json")) {
      if (typeof value === "string") {
        try {
          JSON.parse(value);
          return value;
        } catch {
          return JSON.stringify(value);
        }
      }
      return JSON.stringify(value);
    }

    return String(value);
  }

  /**
   * 오류 메시지 포맷팅
   */
  private formatValidationErrors(errors: FormValidationError[]): string {
    if (errors.length === 0) return "알 수 없는 오류가 발생했습니다.";
    if (errors.length === 1) return errors[0].message;

    return `다음 오류들을 수정해주세요:\n• ${errors.map((e) => e.message).join("\n• ")}`;
  }

  /**
   * 시스템 오류 메시지 포맷팅
   */
  private formatErrorMessage(error: any): string {
    if (error.code === "23505") {
      return "중복된 데이터가 이미 존재합니다.";
    }

    if (error.code === "23503") {
      return "참조 무결성 제약조건을 위반했습니다.";
    }

    if (error.code === "23502") {
      return "필수 입력 항목이 누락되었습니다.";
    }

    if (
      error.message?.includes("relation") &&
      error.message?.includes("does not exist")
    ) {
      return "지정된 테이블이 존재하지 않습니다.";
    }

    return `저장 중 오류가 발생했습니다: ${error.message || error}`;
  }

  /**
   * 캐시 클리어
   */
  public clearCache(): void {
    this.columnCache.clear();
    this.webTypeCache.clear();
    console.log("🧹 동적 폼 서비스 캐시가 클리어되었습니다.");
  }

  /**
   * 테이블별 캐시 클리어
   */
  public clearTableCache(tableName: string): void {
    this.columnCache.delete(tableName);
    this.webTypeCache.delete(tableName);
    console.log(`🧹 테이블 '${tableName}' 캐시가 클리어되었습니다.`);
  }
}

// 싱글톤 인스턴스
export const enhancedDynamicFormService = new EnhancedDynamicFormService();
