<?php

namespace Modules\Lpj\Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Modules\Lpj\Models\Debiture;
use Modules\Location\Models\City;
use Modules\Location\Models\District;
use Modules\Location\Models\Province;
use Modules\Location\Models\Village;
use Modules\Lpj\Models\DokumenJaminan;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\JenisJaminan;
use Modules\Lpj\Models\PemilikJaminan;
use Modules\Lpj\Models\HubunganPemilikJaminan;
use Illuminate\Support\Facades\Log;


class MigrationDokumentJaminanSeeder extends Seeder
{
    protected $errorLogFile = __DIR__ . '/csv/dokumen_20251022_error_log.csv';
    /**
     * Run the database seeds.
     */
    public function run()
    {
        $this->initializeErrorLog();
        // Path ke file csv
        $filePath = realpath(__DIR__ . '/csv/dokumen_20251022.csv');

        if (!$filePath) {
            Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/dokumen_20251022.csv');
            $this->command->error('File csv tidak ditemukan.');
            return;
        }

        if (($handle = fopen($filePath, 'r')) === false) {
            Log::error('Gagal membuka file CSV: ' . $filePath);
            $this->command->error('Gagal membuka file CSV.');
            return;
        }

        $header = fgetcsv($handle, 0, ',');
        $rows = [];
        $batchSize = 1000; // Ukuran batch
        $permohonanCache = [];
        $jenisJaminanCache = [];
        $pemilikJaminanCache = [];
        $provinceCache = [];
        $cityCache = [];
        $districtCache = [];
        $subdistrictCache = [];
        $totalData = 0;
        $errorCount = 0;
        $errorDebitureIds = [];
        $hubunganPemilikCache = [];
        // Menghitung total data di file CSV
        while (($data = fgetcsv($handle, 0, ',')) !== false) {
            $totalData++;
        }

        rewind($handle); // Reset pointer ke awal file
        fgetcsv($handle, 0, ','); // Skip header

        $batchCount = 0;
        $currentRow = 0;


        // Membaca setiap baris dalam CSV
        while (($data = fgetcsv($handle, 0, ',')) !== false) {
            if (count($data) != count($header)) {
                Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
                $errorCount++;
                $errorDebitureIds[] = $data[0] ?? 'ID tidak valid'; // Menyimpan ID yang error
                continue;
            }

            $rows[] = array_combine($header, $data);
            $currentRow++;

            // print_r($rows);


            if (count($rows) >= $batchSize) {
                $errorDebitureIds[] = $data[0] ?? 'ID tidak valid'; // Menyimpan ID yang error
                $this->processBatch($rows, $permohonanCache, $jenisJaminanCache, $pemilikJaminanCache, $provinceCache, $cityCache, $districtCache, $subdistrictCache, $batchCount, $currentRow, $totalData, $errorCount, $errorDebitureIds, $hubunganPemilikCache);
                $rows = [];
            }
        }

        // print_r($rows[0]);
        if (!empty($rows)) {
            $this->processBatch($rows, $permohonanCache, $jenisJaminanCache, $pemilikJaminanCache, $provinceCache, $cityCache, $districtCache, $subdistrictCache, $batchCount, $currentRow, $totalData, $errorCount, $errorDebitureIds, $hubunganPemilikCache);
        }

        fclose($handle);

        $this->command->info('Data debiture berhasil dimigrasikan.');
    }

    /**
     * Proses batch data.
     */

     private function processBatch(
         array $rows,
         array &$permohonanCache,
         array &$jenisJaminanCache,
         array &$pemilikJaminanCache,
         array &$provinceCache,
         array &$cityCache,
         array &$districtCache,
         array &$subdistrictCache,
         int $batchCount,
         int $currentRow,
         int $totalData,
         int &$errorCount,
         array &$errorDebitureIds,
         array &$hubunganPemilikCache
     ) {
         foreach ($rows as $index => $row) {
             try {
                 // Jalankan transaksi per-baris
                // DB::beginTransaction();

                 // Cari permohonan

                 $permohonan = Permohonan::where('nomor_registrasi', $row['mig_nomor_jaminan'])->first();

                 if (empty($permohonan)) {
                     throw new \Exception('Missing debiture_id' . $row['mig_nomor_jaminan']);
                     continue;
                 }

                 // Pastikan permohonan_id belum digunakan di dokumen_jaminan
                 $existingDokumen = DokumenJaminan::where('permohonan_id', $permohonan->id)->first();
                 if ($existingDokumen) {
                     //throw new \Exception("permohonan_id {$permohonan->id} sudah digunakan di dokumen_jaminan");
                     //continue;
                 }

                 // Ambil lokasi

                //  jika external silahkan matikan ini

                 $proviceCode = $this->getProvinceCode($row['mig_province_name'], $provinceCache);
                 $cityCode = $this->getCityCode($row['mig_city_name'], $cityCache);
                 $districtCode = $this->getDistrictCode($row['mig_district_name'], $districtCache);
                 $subdistrict = $this->getSubdistrictCode($row['mig_village_name'], $subdistrictCache, $districtCache);
                //  $hubunganPemilik = $this->getHubunganPemilikJaminanId($row['mig_hubungan_pemilik_jaminan'], $hubunganPemilikCache);


                 $pemilik_jaminan_name = $this->getDebitureId($row['mig_kd_debitur_seq'], $pemilikJaminanCache);
                 // Buat Pemilik Jaminan
                 $pemilik_jaminan = PemilikJaminan::updateOrCreate([
                     'debiture_id' => $permohonan->debiture_id,
                     'hubungan_pemilik_jaminan_id' => 1,
                    //  'name' => $row['name'],
                     'name' => $pemilik_jaminan_name,
                     'detail_sertifikat' => null,
                     'npwp' => null,
                     'nomor_id' => null,
                     'email' => null,
                     'phone' => null,
                    //  jika external silahkan matikan ini
                     'province_code' => $proviceCode,
                     'city_code' => $cityCode,
                     'district_code' => $districtCode,
                     'village_code' => $subdistrict['code'],
                     'postal_code' => $subdistrict['postal_code'],
                     'address' => $row['address'],
                     'created_at' => $this->parseTimestamp($row['created_at']),
                     'updated_at' => $this->parseTimestamp($row['updated_at']),
                     'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
                     'processed_at' => now(),
                     'is_mig' => 1
                 ]);


                 // Buat Dokumen Jaminan
                $dokumenJaminan = DokumenJaminan::updateOrCreate([
                    'permohonan_id' => $permohonan->id,
                    'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
                    'nomor_lpj' => $row['mig_nomor_jaminan']
                 ],[
                     'debiture_id' => $permohonan->debiture_id,
                     'permohonan_id' => $permohonan->id,
                     'jenis_jaminan_id' => $this->getJaminanId($row['mig_jenis_seq']) ?? '',
                     'pemilik_jaminan_id' => $pemilik_jaminan->id,
                    //  jika external silahkan matikan ini
                    'province_code' => $proviceCode ?? '',
                    'city_code' => $cityCode ?? '',
                    'district_code' => $districtCode ?? '',
                    'village_code' => $subdistrict['code'] ?? '',
                    'postal_code' => $subdistrict['postal_code'] ?? '',
                     'address' => $row['address'],
                     'created_at' => $this->parseTimestamp($row['created_at']),
                     'updated_at' => $this->parseTimestamp($row['updated_at']),
                     'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
                     'processed_at' => now(),
                     'nomor_lpj' => $row['mig_nomor_jaminan'],
                     'is_mig' => 1
                 ]);

                 // Commit jika semua sukses
                // DB::commit();
                 $this->command->info("Proses dokumen jaminan: " . $row['mig_kd_debitur_seq'] . "Batch: {$batchCount} Baris: {$currentRow} Total: {$totalData} Error: {$errorCount}");

             } catch (\Exception $e) {
                 // Rollback hanya untuk baris ini
                 Log::error("Error pada baris: " . json_encode($row) . ". Pesan: " . $e->getMessage());
                 $this->logError($row['mig_kd_debitur_seq'] ?? '-', $e->getMessage());
                 $errorDebitureIds[] = $row['mig_kd_debitur_seq'] ?? '-';
                // DB::rollBack();
                 continue;
             }
         }

         $this->command->info("Batch {$batchCount} selesai. Total error: " . count($errorDebitureIds));
     }
    // private function getPermohonanId($code,$cache)
    // {
    //     if (isset($cache[$code])) {
    //         return $cache[$code];
    //     }

    //     $permohonan = Permohonan::where('mig_kd_debitur_seq', $code)->where('nomor_registrasi', $mig_nomor_jaminan)->first();

    //     if ($permohonan) {
    //         $cache[$code] = $permohonan;
    //         return $permohonan;
    //     }

    //     return null;
    // }


    private function getJaminanId($code): ?int
    {
        /*$mapping = [
            7 => 17,
            8 => 13,
            6 => 32,
            1 => 17,
            2 => 26,
            3 => 27,
            4 => 50,
            5 => 21,
            138051314724 => 23,
            138027243057 => 34,
            138027664224 => 35,
            138027738489 => 10,
            138051485796 => 48,
            138051492883 => 47,
            138051515419 => 40,
            138051753311 => 41,
            138051754843 => 46,
            138051759078 => 42,
            138051480538 => 45,
            123382184742 => 18,
            138051483711 => 44,
            991 => 52
        ];*/

        $mapping = [
            1 => 1,
            2 => 17,
            3 => 19,
            4 => 15,
            5 => 18,
            6 => 26,
            7 => 26,
            8 => 26,
            9 => 26,
            10 => 24,
            11 => 28,
            12 => 29,
            13 => 32,
            991001 => 17,
            121965631354 => 17,
            122267387302 => 13,
            122267391891 => 27,
            123242566528 => 1,
            123391628912 => 18,
            123779076991 => 26,
            123779092232 => 26,
            123837866231 => 19,
            124159228236 => 14,
            124280447242 => 32,
            124385048902 => 30,
            124539856281 => 22,
            124635294016 => 13,
            124963468687 => 18,
            125178371127 => 31,
            125228814911 => 17,
            125749523699 => 27,
            126156105725 => 15,
            127407367039 => 15,
            132065123922 => 32,
            138027244724 => 33,
            138027246193 => 34,
            138027693348 => 35,
            138027764236 => 10,
            138050882693 => 15,
            138050910670 => 20,
            138051316169 => 23,
            138051517359 => 36,
            138051519318 => 37,
            138051522331 => 38,
            138051601738 => 39,
            138051602831 => 40,
            138051773783 => 41,
            138051776693 => 46,
            138051780489 => 42,
            164921358499 => 32,
            165216289979 => 49,
            165216294371 => 49,
            173035895092 => 24
        ];

        return $mapping[$code] ?? 17;
    }

    private function getPemilikJaminanId(string $code, array &$cache): ?int
    {
        if (isset($cache[$code])) {
            return $cache[$code];
        }

        $jaminan = PemilikJaminan::where('mig_kd_debitur_seq', $code)->first();

        if ($jaminan) {
            $cache[$code] = $jaminan->id;
            return $jaminan->id;
        }

        return 1;
    }

    private function getDebitureId(string $code, array &$cache): ?string
    {
        if (isset($cache[$code])) {
            return $cache[$code];
        }

        $debiture = Debiture::where('mig_kd_debitur_seq', $code)->first();

        if ($debiture) {
            $cache[$code] = $debiture->name;
            return $debiture->name;
        }

        return null;
    }




    private function getProvinceCode(string $name, array &$cache): ?string
    {

        $normalizedName = strtolower($name);
        if (isset($cache[$normalizedName])) {
            return $cache[$normalizedName];
        }

        $province = Province::whereRaw('LOWER(name) = ?', [strtolower($name)])->first();

        if ($province) {
            $cache[$normalizedName] = $province->code;
            return $province->code;
        }

        return null;
    }

    private function getCityCode(string $name, array &$cache): ?string
    {
        $normalizedName = strtolower($name);
        if (isset($cache[$normalizedName])) {
            return $cache[$normalizedName];
        }

        $city = City::whereRaw('LOWER(name) = ?', [strtolower($name)])->first();

        if ($city) {
            $cache[$normalizedName] = $city->code;
            return $city->code;
        }

        return null;
    }

    private function getDistrictCode(string $name, array &$cache): ?string
    {
        $normalizedName = strtolower($name);
        if (isset($cache[$normalizedName])) {
            return $cache[$normalizedName];
        }

        $district = District::whereRaw('LOWER(name) = ?', [strtolower($name)])->first();

        if ($district) {
            $cache[$normalizedName] = $district->code;
            return $district->code;
        }

        return null;
    }

    private function getSubdistrictCode(string $name, array &$cache, array &$districtCache): ?array
    {
        $normalizedName = strtolower($name);

        // Pastikan cache menyimpan array, bukan hanya kode
        if (isset($cache[$normalizedName])) {
            return $cache[$normalizedName];
        }

        // Ambil subdistrict dari database
        $subdistrict = Village::whereRaw('LOWER(name) = ?', [$normalizedName])->first();

        // Jika ditemukan, simpan ke dalam cache sebagai array lengkap
        if ($subdistrict) {
            $cache[$normalizedName] = [
                'code' => $subdistrict->code,
                'postal_code' => $subdistrict->postal_code
            ];
            return $cache[$normalizedName];
        }

        // Jika tidak ditemukan, kembalikan null
        return [
            'code' => null,
            'postal_code' => null
        ];
    }


    private function getHubunganPemilikJaminanId(string $code, array &$cache): ?int
    {
        if (isset($cache[$code])) {
            return $cache[$code];
        }

        $jaminan = HubunganPemilikJaminan::whereRaw('LOWER(name) = ?', [strtolower($code)])->first();

        if ($jaminan) {
            $cache[$code] = $jaminan->id;
            return $jaminan->id;
        }

        return null;
    }


    /**
     * Mengonversi nilai TIMESTAMP menjadi format datetime yang valid.
     */
    private function parseTimestamp(?string $timestamp): ?string
    {
        try {
            if ($timestamp) {
                // Cek jika format hanya tanggal (Y-m-d)
                if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $timestamp)) {
                    return \Carbon\Carbon::createFromFormat('Y-m-d', $timestamp)
                        ->startOfDay()
                        ->toDateTimeString();
                }
                // Format lengkap (Y-m-d H:i:s)
                if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
                    return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
                }
                // Format d/m/Y H:i:s (contoh: 28/4/2017 14:43:43)
                if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
                    return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
                }
            }
            return null;
        } catch (\Exception $e) {
            Log::error('Gagal memparsing timestamp: ' . $timestamp . '. Pesan: ' . $e->getMessage());
            return null;
        }
    }

    private function initializeErrorLog()
{
    $file = $this->errorLogFile;

    if (file_exists($file)) {
        unlink($file); // Hapus file lama
    }

    $handle = fopen($file, 'w');
    fputcsv($handle, ['mig_kd_debitur_seq', 'Error']);
    fclose($handle);
}

private function logError(string $kode, string $message)
{
    Log::error("Error migrasi dokumen jaminan [$kode]: $message");

    $handle = fopen($this->errorLogFile, 'a');
    fputcsv($handle, [$kode, $message]);
    fclose($handle);
}
}
