<?php

namespace Koneko\SatCatalogs\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;

class CsvDatabaseService
{
    /**
     * Carga datos desde CSV a una tabla de la base de datos.
     */
    public static function importCsvToTable(string $csvFilePath, string $tableName, array $columns, bool $updateExisting = false)
    {
        if (!file_exists($csvFilePath)) {
            return ["error" => "Archivo CSV no encontrado: {$csvFilePath}"];
        }

        $handle = fopen($csvFilePath, 'r');
        $batchSize = 5000;
        $data = [];
        $rowCount = 0;
        $insertedCount = 0;

        while (($row = fgetcsv($handle, 1000, ";")) !== false) {
            $record = [];

            foreach ($columns as $index => $column) {
                $value = $row[$index] ?? null;

                // Aplicar CAST según el tipo de dato
                if ($value === '') {
                    $record[$column] = null;

                } elseif (self::isDateColumn($column)) {
                    $record[$column] = self::formatDate($value);

                } elseif (is_numeric($value)) {
                    $record[$column] = strpos($value, '.') !== false ? (float) $value : (int) $value;

                } else {
                    $record[$column] = trim($value);
                }
            }

            $data[] = $record;
            $rowCount++;

            if (count($data) >= $batchSize) {
                self::insertOrUpdate($tableName, $data, $updateExisting);

                $insertedCount += count($data);
                $data = [];
            }
        }

        fclose($handle);

        if (!empty($data)) {
            self::insertOrUpdate($tableName, $data, $updateExisting);

            $insertedCount += count($data);
        }

        return [
            "inserted" => $insertedCount,
            "total_rows" => $rowCount
        ];
    }

    /**
     * Inserta o actualiza datos en la tabla.
     */
    private static function insertOrUpdate(string $tableName, array $data, bool $updateExisting)
    {
        if (empty($data)) {
            echo "⚠️ Datos vacíos para {$tableName}\n";
            return;
        }

        foreach ($data as &$record) {
            foreach ($record as $key => $value) {
                if ($value === '?') { // Si hay un '?', lo convertimos en NULL
                    $record[$key] = null;
                }
            }
        }
        unset($record);

        try {
            $primaryKeys = self::getPrimaryKeys($tableName);
            $updateFields = array_diff(array_keys($data[0]), $primaryKeys);

            if ($updateExisting && !empty($updateFields)) {
                DB::table($tableName)->upsert($data, $primaryKeys, $updateFields);

                echo "✅ Upsert ejecutado en {$tableName} (" . count($data) . " filas)\n";

            } else {
                DB::table($tableName)->insert($data);

                echo "✅ Insert ejecutado en {$tableName} (" . count($data) . " filas)\n";
            }

        } catch (\Exception $e) {
            Log::error("🚨 Error en {$tableName}: " . $e->getMessage());
            echo "❌ ERROR en {$tableName}: {$e->getMessage()}\n";

            die();
        }
    }



    /**
     * **Detecta si una columna es de tipo fecha.**
     */
    private static function isDateColumn(string $columnName): bool
    {
        $dateFields = ['fecha_inicio_vigencia', 'fecha_fin_vigencia'];

        return in_array($columnName, $dateFields);
    }

    /**
     * **Convierte fechas de diferentes formatos a `YYYY-MM-DD`.**
     */
    private static function formatDate($value)
    {
        if (!$value || strtolower($value) === 'null') {
            return null;
        }

        try {
            if (is_numeric($value) && $value > 10000) {
                return Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value))->format('Y-m-d');
            }

            if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $value)) {
                return Carbon::createFromFormat('d/m/Y', $value)->format('Y-m-d');
            }

            return Carbon::parse($value)->format('Y-m-d');

        } catch (\Exception $e) {
            return null;
        }
    }

    /**
     * **Obtiene las claves primarias de la tabla.**
     */
    private static function getPrimaryKeys(string $tableName): array
    {
        $primaryKeys = [
            'sat_forma_pago'      => ['c_forma_pago'],
            'sat_moneda'          => ['c_moneda'],
            'sat_codigo_postal'   => ['c_codigo_postal'],
            'sat_regimen_fiscal'  => ['c_regimen_fiscal'],
            'sat_pais'            => ['c_pais'],
            'sat_uso_cfdi'        => ['c_uso_cfdi'],
            'sat_clave_prod_serv' => ['c_clave_prod_serv'],
            'sat_clave_unidad'    => ['c_clave_unidad'],
            'sat_aduana'          => ['c_aduana'],
            'sat_colonia'         => ['c_colonia', 'c_codigo_postal'],
            'sat_estado'          => ['c_estado', 'c_pais'],
            'sat_localidad'       => ['c_localidad', 'c_estado'],
            'sat_municipio'       => ['c_municipio', 'c_estado'],
            'sat_banco'           => ['c_banco'],
            'sat_deduccion'       => ['c_deduccion'],
            'sat_percepcion'      => ['c_percepcion'],
            'sat_regimen_contratacion' => ['c_regimen_contratacion'],
        ];

        return $primaryKeys[$tableName] ?? ['id'];
    }
}
