<?php

namespace Modules\Lpj\Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\Debiture;
use Modules\Basicdata\Models\Branch;
use Modules\Usermanagement\Models\User;

class MigrationPermohonanSeeder extends Seeder
{
    protected $errorLogFile = __DIR__ . '/csv/permohonan/permohonan.2024.2025_error.csv';

    public function run()
    {
        // Bersihkan file error sebelum mulai
        $this->initializeErrorLog();

        $filePath = realpath(__DIR__ . '/csv/permohonan/permohonan.2024.2025.csv');

        if (!$filePath) {
            Log::error('File CSV tidak ditemukan: ' . __DIR__ . '/csv/permohonan/permohonan.2024.2025.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;
        $branchCache = [];
        $debitureCache = [];
        $totalData = 0;
        $currentRow = 0;
        $batchCount = 0;

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

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

        while (($data = fgetcsv($handle, 0, ',')) !== false) {
            if (count($data) != count($header)) {
                $nomorJaminan = $data[array_search('mig_mst_jaminan_nomor_jaminan', $header)] ?? '-';
                $this->logError($nomorJaminan, count($data).' Jumlah kolom tidak sesuai dengan header. Header: '.count($header));
                continue;
            }

            $rows[] = array_combine($header, $data);
            $currentRow++;
// print_r($rows);
            if (count($rows) >= $batchSize) {
                $batchCount++;
                $this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
                $this->processBatch($rows, $branchCache, $debitureCache, $totalData, $batchCount);
                $rows = [];
            }
        }

        // print_r($rows);

        if (!empty($rows)) {
            $batchCount++;
            $this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
            $this->processBatch($rows, $branchCache, $debitureCache, $totalData, $batchCount);
        }

        fclose($handle);

        $this->command->info("Migrasi selesai. Total data diproses: $totalData.");
    }

    private function processBatch(array $rows, array &$branchCache, array &$debitureCache, int $totalData, int $batchCount)
    {
        $userData = [];
        foreach ($rows as $index => $row) {
            try {
                $nomorJaminan = $row['mig_mst_jaminan_nomor_jaminan'] ?? '-';

                // Cek apakah sudah diproses
                $existingRecord = Permohonan::where('nomor_registrasi', $row['mig_mst_jaminan_nomor_jaminan'])->first();
                if ($existingRecord && $existingRecord->processed_at) {
                    $this->command->info("Data sudah diproses: $nomorJaminan");
                    //continue;
                }

                // Ambil branch_id
                $branchId = $this->getBranchId($row['mig_mst_jaminan_kd_cabang'] ?? null, $branchCache);
                // if (!$branchId) {
                //     $this->logError($branchId, 'Cabang tidak ditemukan');
                //     continue;
                // }

                // Ambil Debitur ID
                $debitureId = $this->getDebiturId($row['mig_mst_jaminan_kd_debitur_seq'], $debitureCache);
                if (!$debitureId) {
                    $this->logError($nomorJaminan, 'Debitur tidak ditemukan');
                    continue;
                }

                // Ambil User IDs
                $userId = $this->getUserId($row['mig_mst_jaminan_nama_ao'], $branchCache, true);

                // jika external matikan
                $approved1Id = $this->getUserId($row['mig_mst_lpj_user_approved_1'], $branchCache, false);
                $approved2Id = $this->getUserId($row['mig_mst_lpj_user_approved_2'], $branchCache, false);
                $periksaId = $this->getUserId($row['mig_mst_lpj_user_periksa'], $branchCache, false);

                // // Ambil user IDs berdasarkan NIK
                $userIdUpdate = $this->getUserIdData($row['mig_mst_jaminan_user_create'] ?? null, $userData)['id'];
                $userIdOtorisasi = $this->getUserIdData($row['mig_mst_jaminan_user_oto'] ?? null, $userData)['id'];

                // jika external matikan
                //   if (!$userIdUpdate || !$userIdOtorisasi) {
                //       $this->logError($userIdUpdate, 'Salah satu user tidak ditemukan');
                //       continue;
                //   }

                // Mapping field user
                $mapUser = [
                    'created_by' => $userIdUpdate,
                    'updated_by' => $userIdUpdate,
                    'authorized_by' => $userIdOtorisasi,
                ];

                // jika external matikan
                // if (!$userId || !$approved1Id || !$approved2Id) {
                //     $this->logError($userId, 'Salah satu user tidak ditemukan');
                //     continue;
                // }

                // Mapping data
                $jenisFasilitas = $this->checkJenisFasilitas($row['mig_mst_jaminan_kd_jenis_fas_seq']);
                $tujuanPenilaian = $this->checkTujuanPenilaian($row['mig_mst_jaminan_kd_tujuan_seq']);
                $regionId = $this->checkRegion($row['mig_mst_kode_kelompok_region']);


                $nomor_lpj = isset($row['mig_mst_lpj_nomor_lpj']) ? $row['mig_mst_lpj_nomor_lpj'] : '';
                $nomor_lpj = is_numeric($nomor_lpj) ? (int)$nomor_lpj : 0;

                $jenisPenilaian = $row['mig_internal_or_external'] == 1 ? 2 : 1;

                // Simpan data
                $permohonan = Permohonan::updateOrCreate([
                    'nomor_registrasi' => $nomorJaminan,
                ],[
                    'nomor_registrasi' => $nomorJaminan,
                    'tanggal_permohonan' => $this->parseDate($row['tanggal_permohonan']),
                    'user_id' => $userId['id'],
                    'branch_id' => $branchId,
                    'tujuan_penilaian_id' => $tujuanPenilaian,
                    'debiture_id' => $debitureId,
                    'jenis_fasilitas_kredit_id' => $jenisFasilitas,
                    'nilai_plafond_id' => 2,
                    'status' => 'done',
                    // jika external matikan
                    'approval_eo' => $approved1Id['id'] ?? 0,
                    'approval_eo_at' => $this->parseTimestamp($row['mig_mst_lpj_tgl_approved_1']),
                    'approval_dd' => $approved2Id['id'] ?? 0,
                    'approval_dd_at' => $this->parseTimestamp($row['mig_mst_lpj_tgl_approved_2']),
                    'approval_so' => $periksaId['id'] ?? 0,
                    'approval_so_at' => $this->parseTimestamp($row['mig_mst_lpj_tgl_periksa']),
                    // end external matikan
                    'keterangan' => $row['mig_mst_jaminan_catatan'] ?? null,
                    'status_bayar' => 'sudah_bayar',
                    'created_at' => $this->parseTimestamp($row['mig_mst_jaminan_tgl_create']),
                    'updated_at' => $this->parseTimestamp($row['mig_mst_jaminan_tgl_update']),
                    'mig_kd_debitur_seq' => $row['mig_mst_jaminan_kd_debitur_seq'],
                    'nomor_lpj' => $nomor_lpj,
                    'region_id' => $regionId,
                    'jenis_penilaian_id' =>  $jenisPenilaian,
                    'authorized_by' => $mapUser['authorized_by'],
                    'created_by' => $mapUser['created_by'],
                    'updated_by' => $mapUser['updated_by'],
                    'mig_nama_ao' => $row['mig_mst_jaminan_nama_ao'],
                    'mig_permohonan' => json_encode($row),
                    'is_mig' => 1
                ]);

                if($permohonan && $nomorJaminan=='253122'){
                   Log::info($permohonan);
                }

                $this->command->info("Proses data permohonan $nomorJaminan (" . ($index + 1) . '/' . count($rows) . " pada batch ke-$batchCount)");

            } catch (\Exception $e) {
                $nomorJaminan = $row['mig_mst_jaminan_nomor_jaminan'] ?? '-';
                $this->logError($nomorJaminan, "Error eksepsi: " . $e->getMessage());
                continue;
            }
        }
    }

    private function getUserIdData(?string $code, array &$cache): array
    {
        if (!$code) {
            return ['id' => null, 'branch_id' => null];
        }

        if (isset($cache[$code])) {
            return $cache[$code];
        }

        $user = User::where('nik', $code)->first();

        if ($user) {
            $cache[$code] = ['id' => $user->id, 'branch_id' => $user->branch_id];
            return $cache[$code];
        }

        return ['id' => null, 'branch_id' => null];
    }

    private function getUserId(string $value, array &$cache, bool $includeBranch = false): ?array
    {
        if (isset($cache[$value])) {
            return $cache[$value];
        }

        $user = null;

        if ($includeBranch) {
            $user = User::whereRaw('LOWER(name) = ?', [strtolower($value)])->first();
        } else {
            $user = User::where('nik', $value)->first();
        }

        if ($user) {
            $result = ['id' => $user->id];
            if ($includeBranch) {
                $result['branch_id'] = $user->branch_id;
            }
            $cache[$value] = $result;
            return $result;
        }

        return ['id' => null, 'branch_id' => null];
    }

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

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

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

        return null;
    }
    private function getBranchId(?string $code, array &$cache): ?int
    {
        if (!$code) {
            return null;
        }

        if (isset($cache[$code])) {
            return $cache[$code];
        }

        $branch = Branch::where('code', $code)->first();

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

        return null;
    }
    private function checkJenisFasilitas($code): int
    {
        // Extract the integer part before the decimal
        $intCode = (int) $code;

        $mapping = [
            161337594516 => 1,
            161337598118 => 14,
            155739382483 => 7,
            2 => 9,
            153568936592 => 10,
            155737674431 => 11,
            161337561199 => 12,
            1 => 13,
        ];

        return $mapping[$intCode] ?? 0;
    }

    private function checkTujuanPenilaian($code): int
    {
        $code = (int) $code;
        $mapping = [
            1 => 1,
            2 => 2,
            3 => 9,
            4 => 10,
            5 => 8,
            6 => 3,
        ];

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

    private function checkRegion($code): int
    {
        $mapping = [
            '01' => 1,
            '02' => 2,
            '04' => 3,
            '07' => 4,
            '06' => 5,
        ];

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

    private function parseDate(?string $date): ?string
    {
        if (!$date) {
            return null;
        }

        try {
            // Coba format d/m/Y terlebih dahulu (contoh: 30/1/2025)
            return \Carbon\Carbon::createFromFormat('d/m/Y', $date)->toDateString();
        } catch (\Exception $e) {
            try {
                // Fallback ke format Y-m-d jika gagal
                return \Carbon\Carbon::createFromFormat('Y-m-d', $date)->toDateString();
            } catch (\Exception $e2) {
                // If both formats fail, try to parse with Carbon::parse as last resort
                try {
                    return \Carbon\Carbon::parse($date)->toDateString();
                } catch (\Exception $e3) {
                    return null;
                }
            }
        }
    }

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

            // Cek format d/m/Y H:i:s (contoh: 29/9/2025 17:20:36)
            if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{1,2}:\d{2}:\d{2}$/', $timestamp)) {
                return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
            }

            // 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();
            }

            // Fallback: coba parsing dengan createFromFormat tambahan atau gunakan parse
            return \Carbon\Carbon::parse($timestamp)->toDateTimeString();
        } catch (\Exception $e) {
            Log::error('Gagal memparsing timestamp: ' . $timestamp . '. Pesan: ' . $e->getMessage());
            return null;
        }
    }



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

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

        // Buat file baru dengan header
        $handle = fopen($file, 'w');
        fputcsv($handle, ['mig_mst_jaminan_nomor_jaminan', 'Error']);
        fclose($handle);
    }

    private function logError($nomorJaminan, string $message)
    {
        Log::error("Error migrasi permohonan [$nomorJaminan]: $message");

        // Tulis ke file error
        $handle = fopen($this->errorLogFile, 'a');
        fputcsv($handle, [$nomorJaminan, $message]);
        fclose($handle);
    }
}
