import { query } from "../database/db";
import {
  DataMappingConfig,
  InboundMapping,
  OutboundMapping,
  FieldMapping,
  DataMappingResult,
  MappingValidationResult,
  FieldTransform,
  DataType,
} from "../types/dataMappingTypes";

export class DataMappingService {
  constructor() {
    // No prisma instance needed
  }

  /**
   * Inbound 데이터 매핑 처리 (외부 → 내부)
   */
  async processInboundData(
    externalData: any,
    mapping: InboundMapping
  ): Promise<DataMappingResult> {
    const startTime = Date.now();
    const result: DataMappingResult = {
      success: false,
      direction: "inbound",
      recordsProcessed: 0,
      recordsInserted: 0,
      recordsUpdated: 0,
      recordsSkipped: 0,
      errors: [],
      executionTime: 0,
      timestamp: new Date().toISOString(),
    };

    try {
      console.log(`📥 [DataMappingService] Inbound 매핑 시작:`, {
        targetTable: mapping.targetTable,
        insertMode: mapping.insertMode,
        fieldMappings: mapping.fieldMappings.length,
      });

      // 데이터 배열로 변환
      const dataArray = Array.isArray(externalData)
        ? externalData
        : [externalData];
      result.recordsProcessed = dataArray.length;

      // 각 레코드 처리
      for (const record of dataArray) {
        try {
          const mappedData = await this.mapInboundRecord(record, mapping);

          if (Object.keys(mappedData).length === 0) {
            result.recordsSkipped!++;
            continue;
          }

          // 데이터베이스에 저장
          await this.saveInboundRecord(mappedData, mapping);

          if (mapping.insertMode === "insert") {
            result.recordsInserted!++;
          } else {
            result.recordsUpdated!++;
          }
        } catch (error) {
          console.error(`❌ [DataMappingService] 레코드 처리 실패:`, error);
          result.errors!.push(
            `레코드 처리 실패: ${error instanceof Error ? error.message : String(error)}`
          );
          result.recordsSkipped!++;
        }
      }

      result.success =
        result.errors!.length === 0 ||
        result.recordsInserted! > 0 ||
        result.recordsUpdated! > 0;
    } catch (error) {
      console.error(`❌ [DataMappingService] Inbound 매핑 실패:`, error);
      result.errors!.push(
        `매핑 처리 실패: ${error instanceof Error ? error.message : String(error)}`
      );
    }

    result.executionTime = Date.now() - startTime;

    console.log(`✅ [DataMappingService] Inbound 매핑 완료:`, result);
    return result;
  }

  /**
   * Outbound 데이터 매핑 처리 (내부 → 외부)
   */
  async processOutboundData(
    mapping: OutboundMapping,
    filter?: any
  ): Promise<any> {
    console.log(`📤 [DataMappingService] Outbound 매핑 시작:`, {
      sourceTable: mapping.sourceTable,
      fieldMappings: mapping.fieldMappings.length,
      filter,
    });

    try {
      // 소스 데이터 조회
      const sourceData = await this.getSourceData(mapping, filter);

      if (
        !sourceData ||
        (Array.isArray(sourceData) && sourceData.length === 0)
      ) {
        console.log(`⚠️ [DataMappingService] 소스 데이터가 없습니다.`);
        return null;
      }

      // 데이터 매핑
      const mappedData = Array.isArray(sourceData)
        ? await Promise.all(
            sourceData.map((record) => this.mapOutboundRecord(record, mapping))
          )
        : await this.mapOutboundRecord(sourceData, mapping);

      console.log(`✅ [DataMappingService] Outbound 매핑 완료:`, {
        recordCount: Array.isArray(mappedData) ? mappedData.length : 1,
      });

      return mappedData;
    } catch (error) {
      console.error(`❌ [DataMappingService] Outbound 매핑 실패:`, error);
      throw error;
    }
  }

  /**
   * 단일 Inbound 레코드 매핑
   */
  private async mapInboundRecord(
    sourceRecord: any,
    mapping: InboundMapping
  ): Promise<Record<string, any>> {
    const mappedRecord: Record<string, any> = {};

    for (const fieldMapping of mapping.fieldMappings) {
      try {
        const sourceValue = sourceRecord[fieldMapping.sourceField];

        // 필수 필드 체크
        if (
          fieldMapping.required &&
          (sourceValue === undefined || sourceValue === null)
        ) {
          if (fieldMapping.defaultValue !== undefined) {
            mappedRecord[fieldMapping.targetField] = fieldMapping.defaultValue;
          } else {
            throw new Error(
              `필수 필드 '${fieldMapping.sourceField}'가 누락되었습니다.`
            );
          }
          continue;
        }

        // 값이 없으면 기본값 사용
        if (sourceValue === undefined || sourceValue === null) {
          if (fieldMapping.defaultValue !== undefined) {
            mappedRecord[fieldMapping.targetField] = fieldMapping.defaultValue;
          }
          continue;
        }

        // 데이터 변환 적용
        const transformedValue = await this.transformFieldValue(
          sourceValue,
          fieldMapping.dataType,
          fieldMapping.transform
        );

        mappedRecord[fieldMapping.targetField] = transformedValue;
      } catch (error) {
        console.error(
          `❌ [DataMappingService] 필드 매핑 실패 (${fieldMapping.sourceField} → ${fieldMapping.targetField}):`,
          error
        );
        throw error;
      }
    }

    return mappedRecord;
  }

  /**
   * 단일 Outbound 레코드 매핑
   */
  private async mapOutboundRecord(
    sourceRecord: any,
    mapping: OutboundMapping
  ): Promise<Record<string, any>> {
    const mappedRecord: Record<string, any> = {};

    for (const fieldMapping of mapping.fieldMappings) {
      try {
        const sourceValue = sourceRecord[fieldMapping.sourceField];

        // 데이터 변환 적용
        const transformedValue = await this.transformFieldValue(
          sourceValue,
          fieldMapping.dataType,
          fieldMapping.transform
        );

        mappedRecord[fieldMapping.targetField] = transformedValue;
      } catch (error) {
        console.error(
          `❌ [DataMappingService] 필드 매핑 실패 (${fieldMapping.sourceField} → ${fieldMapping.targetField}):`,
          error
        );
        throw error;
      }
    }

    return mappedRecord;
  }

  /**
   * 필드 값 변환
   */
  private async transformFieldValue(
    value: any,
    targetDataType: DataType,
    transform?: FieldTransform
  ): Promise<any> {
    let transformedValue = value;

    // 1. 변환 함수 적용
    if (transform) {
      switch (transform.type) {
        case "constant":
          transformedValue = transform.value;
          break;

        case "format":
          if (targetDataType === "date" && transform.format) {
            transformedValue = this.formatDate(value, transform.format);
          }
          break;

        case "function":
          if (transform.functionName) {
            transformedValue = await this.applyCustomFunction(
              value,
              transform.functionName
            );
          }
          break;
      }
    }

    // 2. 데이터 타입 변환
    return this.convertDataType(transformedValue, targetDataType);
  }

  /**
   * 데이터 타입 변환
   */
  private convertDataType(value: any, targetType: DataType): any {
    if (value === null || value === undefined) return value;

    switch (targetType) {
      case "string":
        return String(value);
      case "number":
        const num = Number(value);
        return isNaN(num) ? null : num;
      case "boolean":
        if (typeof value === "boolean") return value;
        if (typeof value === "string") {
          return (
            value.toLowerCase() === "true" || value === "1" || value === "Y"
          );
        }
        return Boolean(value);
      case "date":
        return new Date(value);
      case "json":
        return typeof value === "string" ? JSON.parse(value) : value;
      default:
        return value;
    }
  }

  /**
   * 날짜 포맷 변환
   */
  private formatDate(value: any, format: string): string {
    const date = new Date(value);
    if (isNaN(date.getTime())) return value;

    // 간단한 날짜 포맷 변환
    switch (format) {
      case "YYYY-MM-DD":
        return date.toISOString().split("T")[0];
      case "YYYY-MM-DD HH:mm:ss":
        return date
          .toISOString()
          .replace("T", " ")
          .replace(/\.\d{3}Z$/, "");
      default:
        return date.toISOString();
    }
  }

  /**
   * 커스텀 함수 적용
   */
  private async applyCustomFunction(
    value: any,
    functionName: string
  ): Promise<any> {
    // 추후 확장 가능한 커스텀 함수들
    switch (functionName) {
      case "upperCase":
        return String(value).toUpperCase();
      case "lowerCase":
        return String(value).toLowerCase();
      case "trim":
        return String(value).trim();
      default:
        console.warn(
          `⚠️ [DataMappingService] 알 수 없는 함수: ${functionName}`
        );
        return value;
    }
  }

  /**
   * Inbound 데이터 저장
   */
  private async saveInboundRecord(
    mappedData: Record<string, any>,
    mapping: InboundMapping
  ): Promise<void> {
    const tableName = mapping.targetTable;

    try {
      switch (mapping.insertMode) {
        case "insert":
          await this.executeInsert(tableName, mappedData);
          break;

        case "upsert":
          await this.executeUpsert(
            tableName,
            mappedData,
            mapping.keyFields || []
          );
          break;

        case "update":
          await this.executeUpdate(
            tableName,
            mappedData,
            mapping.keyFields || []
          );
          break;
      }
    } catch (error) {
      console.error(
        `❌ [DataMappingService] 데이터 저장 실패 (${tableName}):`,
        error
      );
      throw error;
    }
  }

  /**
   * 소스 데이터 조회
   */
  private async getSourceData(
    mapping: OutboundMapping,
    filter?: any
  ): Promise<any> {
    const tableName = mapping.sourceTable;

    try {
      // 동적 테이블 쿼리 (Prisma의 경우 런타임에서 제한적)
      // 실제 구현에서는 각 테이블별 모델을 사용하거나 Raw SQL을 사용해야 함

      let whereClause = {};
      if (mapping.sourceFilter) {
        // 간단한 필터 파싱 (실제로는 더 정교한 파싱 필요)
        console.log(
          `🔍 [DataMappingService] 필터 조건: ${mapping.sourceFilter}`
        );
        // TODO: 필터 조건 파싱 및 적용
      }

      if (filter) {
        whereClause = { ...whereClause, ...filter };
      }

      // Raw SQL을 사용한 동적 쿼리
      const sql = `SELECT * FROM ${tableName}${mapping.sourceFilter ? ` WHERE ${mapping.sourceFilter}` : ""}`;
      console.log(`🔍 [DataMappingService] 쿼리 실행: ${sql}`);

      const result = await query<any>(sql, []);
      return result;
    } catch (error) {
      console.error(
        `❌ [DataMappingService] 소스 데이터 조회 실패 (${tableName}):`,
        error
      );
      throw error;
    }
  }

  /**
   * INSERT 실행
   */
  private async executeInsert(
    tableName: string,
    data: Record<string, any>
  ): Promise<void> {
    const columns = Object.keys(data);
    const values = Object.values(data);
    const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");

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

    console.log(`📝 [DataMappingService] INSERT 실행:`, {
      table: tableName,
      columns,
      query: sql,
    });
    await query(sql, values);
  }

  /**
   * UPSERT 실행
   */
  private async executeUpsert(
    tableName: string,
    data: Record<string, any>,
    keyFields: string[]
  ): Promise<void> {
    if (keyFields.length === 0) {
      throw new Error("UPSERT 모드에서는 키 필드가 필요합니다.");
    }

    const columns = Object.keys(data);
    const values = Object.values(data);
    const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");

    const updateClauses = columns
      .filter((col) => !keyFields.includes(col))
      .map((col) => `${col} = EXCLUDED.${col}`)
      .join(", ");

    const sql = `
      INSERT INTO ${tableName} (${columns.join(", ")}) 
      VALUES (${placeholders})
      ON CONFLICT (${keyFields.join(", ")}) 
      DO UPDATE SET ${updateClauses}
    `;

    console.log(`🔄 [DataMappingService] UPSERT 실행:`, {
      table: tableName,
      keyFields,
      query: sql,
    });
    await query(sql, values);
  }

  /**
   * UPDATE 실행
   */
  private async executeUpdate(
    tableName: string,
    data: Record<string, any>,
    keyFields: string[]
  ): Promise<void> {
    if (keyFields.length === 0) {
      throw new Error("UPDATE 모드에서는 키 필드가 필요합니다.");
    }

    const updateColumns = Object.keys(data).filter(
      (col) => !keyFields.includes(col)
    );
    const updateClauses = updateColumns
      .map((col, i) => `${col} = $${i + 1}`)
      .join(", ");

    const whereConditions = keyFields
      .map((field, i) => `${field} = $${updateColumns.length + i + 1}`)
      .join(" AND ");

    const values = [
      ...updateColumns.map((col) => data[col]),
      ...keyFields.map((field) => data[field]),
    ];

    const sql = `UPDATE ${tableName} SET ${updateClauses} WHERE ${whereConditions}`;

    console.log(`✏️ [DataMappingService] UPDATE 실행:`, {
      table: tableName,
      keyFields,
      query: sql,
    });
    await query(sql, values);
  }

  /**
   * 매핑 설정 검증
   */
  validateMappingConfig(config: DataMappingConfig): MappingValidationResult {
    const result: MappingValidationResult = {
      isValid: true,
      errors: [],
      warnings: [],
    };

    if (config.direction === "none") {
      return result;
    }

    // Inbound 매핑 검증
    if (
      (config.direction === "inbound" ||
        config.direction === "bidirectional") &&
      config.inboundMapping
    ) {
      if (!config.inboundMapping.targetTable) {
        result.errors.push("Inbound 매핑에 대상 테이블이 필요합니다.");
      }
      if (config.inboundMapping.fieldMappings.length === 0) {
        result.errors.push("Inbound 매핑에 필드 매핑이 필요합니다.");
      }
      if (
        config.inboundMapping.insertMode !== "insert" &&
        (!config.inboundMapping.keyFields ||
          config.inboundMapping.keyFields.length === 0)
      ) {
        result.errors.push("UPSERT/UPDATE 모드에서는 키 필드가 필요합니다.");
      }
    }

    // Outbound 매핑 검증
    if (
      (config.direction === "outbound" ||
        config.direction === "bidirectional") &&
      config.outboundMapping
    ) {
      if (!config.outboundMapping.sourceTable) {
        result.errors.push("Outbound 매핑에 소스 테이블이 필요합니다.");
      }
      if (config.outboundMapping.fieldMappings.length === 0) {
        result.errors.push("Outbound 매핑에 필드 매핑이 필요합니다.");
      }
    }

    result.isValid = result.errors.length === 0;
    return result;
  }

  /**
   * 리소스 정리
   */
  async disconnect(): Promise<void> {
    // No disconnect needed for raw queries
  }
}
