<?php

namespace Modules\Lpj\Database\Seeders;

use Illuminate\Database\Seeder;
use Modules\Lpj\Models\DokumenJaminan;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\Penilai;
use Modules\Lpj\Models\Laporan;
use Illuminate\Support\Facades\Log;

class MigrationPenilaiSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    protected $errorLogFile = __DIR__ . '/csv/penilaian/penilai.fix.latest_error.csv';
    public function run()
    {
        $this->initializeErrorLog();
        // Path ke file csv
        $filePath = realpath(__DIR__ . '/csv/penilaian/penilai.fix.latest_20251011.csv');

        if (!$filePath) {
            Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/penilaian/penilai.fix.latest_20251011.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 = 500;
        $userData = [];
        $branchCache = []; // <-- Gunakan sebagai cache
        $totalData = 0;

        while (($data = fgetcsv($handle, 0, ',')) !== false) {
            $totalData++;
        }

        rewind($handle);
        fgetcsv($handle, 0, ','); // Skip header

        $batchCount = 0;
        $currentRow = 0;
        $errorCount = 0;
        $errorDebitureIds = [];

        while (($data = fgetcsv($handle, 0, ',')) !== false) {
            if (count($data) != count($header)) {
                Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
                continue;
            }

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

            if (count($rows) >= $batchSize) {
                $batchCount++;
                $this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
                $this->processBatch($rows, $branchCache, $userData, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow);
                $rows = [];
            }
        }
        // info_harga_laporan_202505021522.csv
        // print_r($rows);
        if (!empty($rows)) {
            $batchCount++;
            $this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
            $this->processBatch($rows, $branchCache, $userData, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow);
        }

        fclose($handle);
        $this->command->info("Data debiture berhasil dimigrasikan. Total data: {$totalData}, Total batch: {$batchCount}");
    }


    private function processBatch(array $rows, array &$branchCache, array &$userData, int &$errorCount, array &$errorDebitureIds, int $totalData, int $batchCount, int $currentRow)
    {
        // Kelompokkan berdasarkan mig_nomor_lpj
        $groupedRows = $this->groupRowsByLpj($rows);

        foreach ($groupedRows as $nomorLpj => $groupRows) {
            try {
                // Ambil informasi permohonan dan dokument_id
                $nomorRegis = $this->getNomorRegistrasiPermohonan($nomorLpj, $branchCache);
                if (!$nomorRegis) {
                    Log::warning("Nomor registrasi tidak ditemukan untuk nomor LPJ: {$nomorLpj}");
                    $errorCount++;
                    $errorDebitureIds[] = $nomorLpj;
                    //continue;
                }

                // Dapatkan type_penilai (misal: standar, sederhana)
                $firstRow = reset($groupRows);

                $typePenilai = $this->checkTypePenilai($firstRow['mig_kode_jenis_laporan'] ?? '');

                if (!$typePenilai) {
                    Log::warning("Tidak ada jenis laporan valid untuk nomor LPJ: {$nomorLpj}");
                    $errorCount++;
                    $errorDebitureIds[] = $nomorLpj;
                    //continue;
                }

                // Bangun JSON type_penilai
                $penilaiJson = $this->cekJenisPenilai($groupRows);

                // Simpan ke tabel Penilai

                // print_r(json_decode($penilaiJson, true));

                // Mapping field JSON berdasarkan tipe penilaian
                $fillableFieldMap = [
                    'memo' => 'memo',
                    'standar' => 'lpj',
                    'sederhana' => 'lpj',
                    'call_report' => 'call_report',
                    'rap' => 'rap',
                    'resume' => 'resume'
                ];

                $fieldToUpdate = $fillableFieldMap[$typePenilai] ?? null;

                if (!$fieldToUpdate) {
                    Log::warning("Field tidak dikenali untuk tipe: {$typePenilai}");
                    $errorCount++;
                    $errorDebitureIds[] = $nomorLpj;
                    //continue;
                }

                // NO: 001/241917/LPJ/PJ-2251/VII/24
                // 001 => kode cabang
                // 241917 => nomor lpj
                // LPJ => jenis laporan
                // PJ-2251 => nomor registrasi ambil nilai akhirnnya 242251
                // VII => bulan
                // 24 => tahun

                // Generate nomor_laporan
                $mig_permohonan = json_decode($nomorRegis['mig_permohonan'],true) ?? '';
                $tanggal = $mig_permohonan['mig_mst_lpj_tgl_laporan'] ?? $mig_permohonan['mig_mst_lpj_tgl_oto'] ?? $firstRow['mig_created_at'];

                //$tanggal = $firstRow['mig_created_at'];
                $nomorD = $nomorRegis['nomor_registrasi'];
                $nomorRegistrasi = "PJ-{$nomorD}";
                $nomorLaporan = $this->generateNomorLaporan($typePenilai, $nomorLpj, $nomorRegistrasi, $tanggal, $nomorRegis['id']);
                // Simpan atau update ke tabel Penilai
                $penilaiLP = Penilai::updateOrCreate(
                    [
                         'permohonan_id' => $nomorRegis['id'],
                         'dokument_id' => $nomorRegis['dokument_id']
                     ],
                    [
                         'type' => $typePenilai,
                         $fieldToUpdate => $penilaiJson,
                         'type_penilai' => $typePenilai,
                         'created_at' => $this->parseTimestamp($tanggal),
                         'updated_at' => $this->parseTimestamp($tanggal),
                     ]
                );

                // Simpan ke tabel Laporan
                Laporan::updateOrCreate(
                    [
                        'permohonan_id' => $penilaiLP->permohonan_id,
                        'dokumen_jaminan_id' => $penilaiLP->dokument_id
                    ],
                    [
                        'nomor_laporan' => $nomorLaporan,
                        'created_at' => $this->parseTimestamp($tanggal),
                        'updated_at' => $this->parseTimestamp($tanggal)
                    ]
                );


                $this->command->info("Berhasil simpan data penilai untuk nomor LPJ: {$nomorLpj}");

            } catch (\Exception $e) {
                Log::error("Error pada nomor LPJ {$nomorLpj}: " . $e->getMessage());
                $errorCount++;
                $errorDebitureIds[] = $nomorLpj;
                continue;
            }
        }
    }


    private function getNomorRegistrasiPermohonan($nomorLpj, array &$cache)
    {
        if (isset($cache[$nomorLpj])) {
            return $cache[$nomorLpj];
        }

        $permohonan = Permohonan::where('nomor_lpj', $nomorLpj)->first();

        if (!$permohonan) {
            $cache[$nomorLpj] = null;
            return null;
        }

        $dokumenJaminan = DokumenJaminan::where('permohonan_id', $permohonan->id)->first();

        $result = [
            'id' => $permohonan->id,
            'dokument_id' => $dokumenJaminan ? $dokumenJaminan->id : null,
            'nomor_registrasi' => $permohonan->nomor_registrasi,
            'mig_permohonan' => $permohonan->mig_permohonan
        ];

        $cache[$nomorLpj] = $result;

        return $result;
    }
    private function groupRowsByLpj(array $rows): array
    {
        $grouped = [];

        foreach ($rows as $row) {
            $nomorLpj = $row['mig_nomor_lpj'] ?? null;

            if (!empty($nomorLpj)) {
                $grouped[$nomorLpj][] = $row;
            }
        }

        return $grouped;
    }

    private function checkTypePenilai($type)
    {
        $data = [
            'MAK' => 'memo',
            'STD' => 'standar',
            'SPL' => 'sederhana',
            'RHP' => 'call-report',
            'RAP' => 'rap',
            'PRG' => 'resume',
        ];

        return $data[$type];
    }





    private function cekJenisPenilai(array $groupRows)
    {
        // Urutkan grup berdasarkan mig_urutan_seq
        usort($groupRows, function ($a, $b) {
            return ($a['mig_urutan_seq'] ?? 999) <=> ($b['mig_urutan_seq'] ?? 999);
        });

        // Inisialisasi struktur JSON
        $penilaiJson = [
            'luas_bangunan' => null,
            'luas_tanah' => null,
            'nilai_tanah_1' => null,
            'nilai_tanah_2' => null,
            'sarana_pelengkap_penilai' => null,
            'nilai_sarana_pelengkap_1' => null,
            'nilai_sarana_pelengkap_2' => null,
            'total_nilai_pasar_wajar' => null,
            'likuidasi' => null,
            'likuidasi_nilai_1' => null,
            'likuidasi_nilai_2' => null,
            'asuransi_luas_bangunan' => null,
            'asuransi_nilai_1' => null,
            'asuransi_nilai_2' => "0",
            'npw_tambahan' => []
        ];

        // Ambil mainRow (urutan pertama)
        $mainRow = null;
        foreach ($groupRows as $row) {
            if (($row['mig_urutan_seq'] ?? '') == 1) {
                $mainRow = $row;
                break;
            }
        }

        // Jika tidak ada mainRow, ambil baris pertama sebagai fallback
        if (!$mainRow && !empty($groupRows[0])) {
            $mainRow = $groupRows[0];
        }

        if ($mainRow) {
            $penilaiJson['total_nilai_pasar_wajar'] = $mainRow['mig_nilai_total_nilai_pasar'] ?? null;
            $penilaiJson['likuidasi'] = $mainRow['mig_nilai_likudasi'] ?? null;

            // Hitung likuidasi nilai 2 jika ada total dan persen likuidasi
            $totalPasarWajar = (int)str_replace('.', '', $mainRow['mig_nilai_total_nilai_pasar'] ?? 0);
            $persenLikuidasi = (int)($mainRow['mig_nilai_likudasi'] ?? 0);
            $penilaiJson['likuidasi_nilai_1'] = $mainRow['mig_nilai_total_nilai_pasar'] ?? null;
            $penilaiJson['likuidasi_nilai_2'] = number_format(
                $totalPasarWajar * ($persenLikuidasi / 100),
                0,
                '',
                ''
            );

            // Isi data utama hanya untuk urutan 1
            $penilaiJson['luas_tanah'] = $mainRow['mig_nilai_satuan'] ?? null;
            $penilaiJson['nilai_tanah_1'] = $mainRow['mig_harga_satuan'] ?? null;
            $penilaiJson['nilai_tanah_2'] = number_format(
                (int)str_replace('.', '', $mainRow['mig_nilai_satuan'] ?? 0) *
                (int)str_replace('.', '', $mainRow['mig_harga_satuan'] ?? 0),
                0,
                '',
                ''
            );
        }

        // Proses tambahan (urutan > 1)
        foreach ($groupRows as $row) {
            // Hanya proses jika mig_urutan_seq ada
            $urutan = $row['mig_urutan_seq'] ?? '';
            if (empty($urutan)) {
                continue;
            }

            // Tambahkan ke npw_tambahan
            $penilaiJson['npw_tambahan'][] = [
                'name' => $row['mig_keterangan'] ?? 'Luas Bangunan Tambahan',
                'luas' => $row['mig_nilai_satuan'] ?? null,
                'nilai_1' => $row['mig_harga_satuan'] ?? null,
                'nilai_2' => number_format(
                    (int)str_replace('.', '', $row['mig_nilai_satuan'] ?? 0) *
                    (int)str_replace('.', '', $row['mig_harga_satuan'] ?? 0),
                    0,
                    '',
                    ''
                )
            ];
        }

        // Kosongkan npw_tambahan jika kosong
        if (empty($penilaiJson['npw_tambahan'])) {
            $penilaiJson['npw_tambahan'] = [];
        }

        return json_encode($penilaiJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    }

    private function convertToRoman($month)
    {
        $roman = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII'];
        return $month >= 1 && $month <= 12 ? $roman[$month - 1] : '';
    }

    private function generateNomorLaporan($typePenilai, $nomorLpj, $nomorRegistrasi, $tanggal)
    {
        // Mapping type_penilai ke singkatan laporan
        $laporanMap = [
            'memo' => 'MEMO',
            'standar' => 'LPJ',
            'sederhana' => 'LPJ',
            'call_report' => 'CALL',
            'rap' => 'RAP',
            'resume' => 'RESUME'
        ];

        // Dapatkan tahun dan bulan dari tanggal
        $tanggal_ = $this->parseTimestamp($tanggal);
        $date = \Carbon\Carbon::parse($tanggal_);
        $kodeCabang = '001'; // bisa diambil dari user atau parameter lain jika dinamis
        $tahun = substr($date->year, -2); // 2024 → 24
        $bulan = $this->convertToRoman($date->month); // 7 → VII

        // Format akhir nomor registrasi (PJ-XXX)
        $nomorDebiturAkhir = substr($nomorRegistrasi, -4); // PJ-2251 → 2251

        return sprintf(
            "%s/%s/%s/%s/%s/%s",
            $kodeCabang,
            $nomorLpj,
            $laporanMap[$typePenilai] ?? strtoupper($typePenilai),
            "PJ-" . $nomorDebiturAkhir,
            $bulan,
            $tahun
        );
    }

    private function parseTimestamp(?string $timestamp): ?string
    {
        if (!$timestamp) {
            return null;
        }

        // Trim whitespace dan normalize
        $timestamp = trim($timestamp);

        // Log untuk debugging
        Log::info('Mencoba parsing timestamp: "' . $timestamp . '"');

        // Parsing dengan DateTime native PHP untuk lebih robust
        try {
            // Pattern untuk format d/m/Y H:i:s
            if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/', $timestamp, $matches)) {
                $day = (int) $matches[1];
                $month = (int) $matches[2];
                $year = (int) $matches[3];
                $hour = (int) $matches[4];
                $minute = (int) $matches[5];
                $second = (int) $matches[6];

                // Validasi nilai
                if ($day >= 1 && $day <= 31 && $month >= 1 && $month <= 12 && $year >= 1900 && $year <= 2100 &&
                    $hour >= 0 && $hour <= 23 && $minute >= 0 && $minute <= 59 && $second >= 0 && $second <= 59) {

                    // Buat DateTime object langsung
                    $dateTime = new \DateTime();
                    $dateTime->setDate($year, $month, $day);
                    $dateTime->setTime($hour, $minute, $second);

                    $result = $dateTime->format('Y-m-d H:i:s');
                    Log::info('Berhasil parsing dengan DateTime: ' . $timestamp . ' -> ' . $result);
                    return $result;
                }
            }

            // Pattern untuk format d/m/Y tanpa waktu
            if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', $timestamp, $matches)) {
                $day = (int) $matches[1];
                $month = (int) $matches[2];
                $year = (int) $matches[3];

                // Validasi nilai
                if ($day >= 1 && $day <= 31 && $month >= 1 && $month <= 12 && $year >= 1900 && $year <= 2100) {

                    // Buat DateTime object langsung
                    $dateTime = new \DateTime();
                    $dateTime->setDate($year, $month, $day);
                    $dateTime->setTime(0, 0, 0);

                    $result = $dateTime->format('Y-m-d H:i:s');
                    Log::info('Berhasil parsing tanpa waktu dengan DateTime: ' . $timestamp . ' -> ' . $result);
                    return $result;
                }
            }
        } catch (\Exception $e) {
            Log::error('Gagal parsing dengan DateTime: ' . $timestamp . '. Error: ' . $e->getMessage());
        }

        // Fallback ke format Carbon standar untuk format lainnya
        $formats = [
            'Y-m-d H:i:s',
            'Y-m-d',
            'd-m-Y H:i:s',
            'd-m-Y',
            'j-n-Y H:i:s',
            'j-n-Y',
        ];

        foreach ($formats as $format) {
            try {
                $carbon = \Carbon\Carbon::createFromFormat($format, $timestamp);

                if ($carbon && $carbon->format($format) === $timestamp) {
                    // Jika format tidak mengandung waktu, set ke awal hari
                    if (!str_contains($format, 'H:i:s')) {
                        $carbon = $carbon->startOfDay();
                    }
                    Log::info('Berhasil parsing dengan format ' . $format . ': ' . $timestamp . ' -> ' . $carbon->toDateTimeString());
                    return $carbon->toDateTimeString();
                }
            } catch (\Exception $e) {
                // Lanjut ke format berikutnya
                continue;
            }
        }

        Log::error('Tidak dapat memparsing timestamp dengan format apapun: "' . $timestamp . '"');
        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);
    }
}
