<?php

namespace Modules\Lpj\Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Modules\Lpj\Models\HubunganPemilikJaminan;
use Modules\Lpj\Models\Debiture;
use Modules\Lpj\Models\PemilikJaminan;
use Modules\Basicdata\Models\Branch;
use Modules\Usermanagement\Models\User;
use Illuminate\Support\Facades\Log;
use Modules\Location\Models\Province;
use Modules\Location\Models\City;
use Modules\Location\Models\District;
use Modules\Location\Models\Village;

class MigrationPemilikJaminanSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run()
    {
        $filePath = realpath(__DIR__ . '/csv/pemilik_20251002.csv');

        if (!$filePath) {
            Log::error('File CSV tidak ditemukan: ' . __DIR__ . '/csv/pemilik_20251002.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 = 100; // Ukuran batch
        $branchCache = [];
        $debitureCache = [];
        $hubunganPemilikCache = [];
        $provinceCache = [];
        $cityCache = [];
        $districtCache = [];
        $subdistrictCache = [];

        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);

            if (count($rows) >= $batchSize) {
                $this->processBatch($rows, $branchCache, $debitureCache, $hubunganPemilikCache, $provinceCache, $cityCache, $districtCache, $subdistrictCache);
                $rows = [];
            }
        }

        // print_r($rows);
        if (!empty($rows)) {
            $this->processBatch($rows, $branchCache, $debitureCache, $hubunganPemilikCache, $provinceCache, $cityCache, $districtCache, $subdistrictCache);
        }

        fclose($handle);

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

    private function processBatch(array $rows, array &$branchCache, array &$debitureCache, array &$hubunganPemilikCache, array &$provinceCache, array &$cityCache, array &$districtCache, array &$subdistrictCache)
    {
        foreach ($rows as $index => $row) {
            $debitureId = $this->getDebiturId($row['mig_kd_debitur_seq'], $debitureCache);

            if (!$debitureId) {
                Log::warning('Debitur tidak ditemukan untuk kode: ' . $row['mig_kd_debitur_seq']);
                continue;
            }

            $hubunganPemilik = $this->getHubunganPemilikId($row['mig_hubungan_pemilik_jaminan'], $hubunganPemilikCache);


            $proviceCode = $this->getProvinceCode($row['mig_province_name'], $provinceCache);
            $cityCode = $this->getCityCode($row['mig_city_name'], $cityCache);
            $districtCode = $this->getDistrictCode($row['mig_district_name'], $districtCache);

            $subdistrictCode = $this->getSubdistrictCode($row['mig_village_name'], $subdistrictCache, $districtCache);


            PemilikJaminan::updateOrCreate([
                'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
            ],[
                'debiture_id' => $debitureId,
                'hubungan_pemilik_jaminan_id' => $hubunganPemilik,
                'name' => $row['name'],
                'detail_sertifikat' => null,
                'npwp' => null,
                'nomor_id' => null,
                'email' => null,
                'phone' => null,
                'province_code' => $proviceCode,
                'city_code' => $cityCode,
                'district_code' => $districtCode,
                'village_code' => $subdistrictCode['code'],
                'postal_code' => $subdistrictCode['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
            ]);

            $this->command->info('Proses data Pemilik Jaminan ' . $row['name'] . ' (' . ($index + 1) . '/' . count($rows) . ')');
        }
    }

    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 getHubunganPemilikId(string $name, array &$cache): ?int
    {
        // Normalisasi nama
        $normalizedName = strtolower(trim($name));

        // Cek cache untuk menghindari query berulang
        if (isset($cache[$normalizedName])) {
            return $cache[$normalizedName];
        }

        // Query database dengan pengamanan tambahan
        $hubunganPemilik = HubunganPemilikJaminan::whereRaw('LOWER(name) = ?', [$normalizedName])
            ->whereNull('deleted_at') // Tambahkan ini jika Anda menggunakan soft deletes
            ->first();

        // Jika data ditemukan, simpan dalam cache
        if ($hubunganPemilik) {
            $cache[$normalizedName] = $hubunganPemilik->id;
            return $hubunganPemilik->id;
        }

        // Default jika data tidak ditemukan
        $cache[$normalizedName] = 1; // Cache nilai default untuk menghindari query berulang
        return 1;
    }


    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 parseDate(?string $date): ?string
    {
        try {
            return $date ? \Carbon\Carbon::createFromFormat('Y-m-d', $date)->toDateString() : null;
        } catch (\Exception $e) {
            Log::error('Gagal memparsing tanggal: ' . $date);
            return null;
        }
    }

    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;
        }
    }

}
