const db = require("../config/database");

class Transaction {
  static async create(transactionData) {
    // PRIMERO: Verificar si ya existe una transacción con esta referencia
    const existingTransaction = await this.findByReference(
      transactionData.reference
    );

    if (existingTransaction) {
      console.log("🔄 Transacción existente encontrada:", {
        id: existingTransaction.id,
        reference: existingTransaction.reference,
        status: existingTransaction.status,
      });

      // Si ya existe y está completada, retornar la existente
      if (existingTransaction.status === "completed") {
        console.log("✅ Transacción ya completada, retornando existente");
        return {
          transactionId: existingTransaction.id,
          existing: true,
          status: "completed",
          //data: existingTransaction,
        };
      }

      // Si existe pero está fallida, podemos reintentar
      if (existingTransaction.status === "failed") {
        console.log("🔄 Transacción fallida encontrada, permitiendo reintento");
        // En este caso, continuamos con el proceso normal
      }
    }

    await this.validateTransactionData(transactionData);

    const {
      reference,
      bank_id,
      transaction_type,
      amount,
      currency = "VES",
      payer_identification = null,
      payer_phone = null,
      beneficiary_phone = null,
      beneficiary_identification = null,
      bank_origin = null,
      payment_date = null,
      status = "pending",
      bank_response_code = null,
      bank_response_message = null,
      raw_request_data = null,
      raw_response_data = null,
      bank_transaction_id = null,
    } = transactionData;

    // Asegurar que ningún campo sea undefined
    const safeData = {
      reference,
      bank_id,
      transaction_type,
      amount,
      currency,
      payer_identification,
      payer_phone,
      beneficiary_phone,
      beneficiary_identification,
      bank_origin,
      payment_date,
      status,
      bank_response_code,
      bank_response_message,
      raw_request_data: raw_request_data
        ? JSON.stringify(raw_request_data)
        : null,
      raw_response_data: raw_response_data
        ? JSON.stringify(raw_response_data)
        : null,
      bank_transaction_id,
    };

    // Convertir cualquier undefined a null
    Object.keys(safeData).forEach((key) => {
      if (safeData[key] === undefined) {
        safeData[key] = null;
      }
    });

    try {
      const [result] = await db.execute(
        `INSERT INTO transactions (
          reference, bank_id, transaction_type, amount, currency,
          payer_identification, payer_phone, beneficiary_phone,
          beneficiary_identification, bank_origin, payment_date,
          status, bank_response_code, bank_response_message,
          raw_request_data, raw_response_data, bank_transaction_id
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          safeData.reference,
          safeData.bank_id,
          safeData.transaction_type,
          safeData.amount,
          safeData.currency,
          safeData.payer_identification,
          safeData.payer_phone,
          safeData.beneficiary_phone,
          safeData.beneficiary_identification,
          safeData.bank_origin,
          safeData.payment_date,
          safeData.status,
          safeData.bank_response_code,
          safeData.bank_response_message,
          safeData.raw_request_data,
          safeData.raw_response_data,
          safeData.bank_transaction_id,
        ]
      );

      return {
        transactionId: result.insertId,
        existing: false,
        status: "new",
      };
    } catch (error) {
      console.error("Error en Transaction.create:", error);

      if (error.code === "ER_DUP_ENTRY") {
        const existing = await this.findByReference(reference);
        if (existing) {
          console.log(
            "🔄 Duplicado manejado, retornando transacción existente"
          );
          return {
            transactionId: existing.id,
            existing: true,
            status: existing.status,
            data: existing,
          };
        }
      }

      throw new Error(`Error creando transacción: ${error.message}`);
    }
  }

  // Buscar por transacción bancaria única
  static async findByBankTransaction(bankId, bankTransactionId) {
    try {
      const [rows] = await db.execute(
        `SELECT * FROM transactions 
         WHERE bank_id = ? AND bank_transaction_id = ?`,
        [bankId, bankTransactionId]
      );
      return rows[0];
    } catch (error) {
      console.error("Error en findByBankTransaction:", error);
      return null;
    }
  }

  // Buscar transacciones fallidas para reintentar
  static async findFailedTransactions(bankId, reference, hours = 24) {
    try {
      const [rows] = await db.execute(
        `SELECT * FROM transactions 
         WHERE bank_id = ? AND reference = ? AND status = 'failed'
         AND created_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
         ORDER BY created_at DESC
         LIMIT 1`,
        [bankId, reference, hours]
      );
      return rows[0];
    } catch (error) {
      console.error("Error en findFailedTransactions:", error);
      return null;
    }
  }

  // Incrementar contador de reintentos
  static async incrementRetryCount(transactionId) {
    try {
      const [result] = await db.execute(
        `UPDATE transactions 
         SET retry_count = retry_count + 1, last_retry_at = NOW()
         WHERE id = ?`,
        [transactionId]
      );
      return result.affectedRows > 0;
    } catch (error) {
      console.error("Error incrementing retry count:", error);
      return false;
    }
  }

  static async findByReference(reference) {
    try {
      const [rows] = await db.execute(
        `SELECT t.*, b.name as bank_name, b.code as bank_code 
         FROM transactions t 
         LEFT JOIN banks b ON t.bank_id = b.id 
         WHERE reference = ?`,
        [reference]
      );
      return rows[0] || null;
    } catch (error) {
      console.error("Error en Transaction.findByReference:", error);
      return null;
    }
  }

  static async checkExistingTransaction(reference, bankId) {
    try {
      const [rows] = await db.execute(
        `SELECT id, status, bank_response_code, bank_response_message, amount
         FROM transactions 
         WHERE reference = ? AND bank_id = ? 
         ORDER BY created_at DESC 
         LIMIT 1`,
        [reference, bankId]
      );

      if (rows.length > 0) {
        const tx = rows[0];
        return {
          exists: true,
          id: tx.id,
          status: tx.status,
          responseCode: tx.bank_response_code,
          responseMessage: tx.bank_response_message,
          amount: tx.amount,
        };
      }

      return { exists: false };
    } catch (error) {
      console.error("Error en checkExistingTransaction:", error);
      return { exists: false, error: error.message };
    }
  }

  static async updateStatus(reference, status, bankResponse = null) {
    try {
      let query =
        "UPDATE transactions SET status = ?, updated_at = CURRENT_TIMESTAMP";
      const params = [status];

      if (bankResponse) {
        query +=
          ", bank_response_code = ?, bank_response_message = ?, raw_response_data = ?";
        params.push(
          bankResponse.code || null,
          bankResponse.message || null,
          bankResponse ? JSON.stringify(bankResponse) : null
        );
      }

      query += " WHERE reference = ?";
      params.push(reference);

      const [result] = await db.execute(query, params);
      return result.affectedRows > 0;
    } catch (error) {
      console.error("Error en Transaction.updateStatus:", error);
      throw error;
    }
  }

  static async validateTransactionData(transactionData) {
    const required = ["reference", "bank_id", "transaction_type", "amount"];
    const missing = required.filter((field) => {
      const value = transactionData[field];
      return value === undefined || value === null || value === "";
    });

    if (missing.length > 0) {
      throw new Error(`Campos requeridos faltantes: ${missing.join(", ")}`);
    }

    // Validar tipos de datos
    if (
      typeof transactionData.reference !== "string" ||
      transactionData.reference.length < 5
    ) {
      throw new Error(
        "La referencia debe ser un string de al menos 5 caracteres"
      );
    }

    if (
      typeof transactionData.bank_id !== "number" ||
      transactionData.bank_id <= 0
    ) {
      throw new Error("bank_id debe ser un número positivo");
    }

    if (
      ![
        "pago_movil",
        "transferencia",
        "consulta",
        "transferencia_cuenta",
      ].includes(transactionData.transaction_type)
    ) {
      throw new Error(
        "transaction_type debe ser: pago_movil, transferencia o consulta"
      );
    }

    if (
      typeof transactionData.amount !== "number" ||
      transactionData.amount <= 0
    ) {
      throw new Error("El campo amount debe ser un número positivo");
    }

    // Validar campos opcionales si están presentes
    if (
      transactionData.payer_identification &&
      typeof transactionData.payer_identification !== "string"
    ) {
      throw new Error("payer_identification debe ser un string");
    }

    if (
      transactionData.payer_phone &&
      typeof transactionData.payer_phone !== "string"
    ) {
      throw new Error("payer_phone debe ser un string");
    }

    if (
      transactionData.beneficiary_phone &&
      typeof transactionData.beneficiary_phone !== "string"
    ) {
      throw new Error("beneficiary_phone debe ser un string");
    }

    if (
      transactionData.payment_date &&
      !this.isValidDate(transactionData.payment_date)
    ) {
      throw new Error("payment_date debe ser una fecha válida");
    }

    return true;
  }

  static isValidDate(dateString) {
    try {
      const date = new Date(dateString);
      return date instanceof Date && !isNaN(date);
    } catch {
      return false;
    }
  }
}

module.exports = Transaction;
