<?php

namespace Database\Seeders;

use App\Models\IndividualRegistration;
use App\Models\User;
use App\Services\NinEncryptionService;
use Carbon\Carbon;
use Illuminate\Database\Seeder;

class DemoIndividualRegistrationSeeder extends Seeder
{
    private const TOTAL = 362;

    private const BATCH_SIZE = 50;

    private static array $maleFirstNames = [
        'Emeka', 'Chukwuemeka', 'Obinna', 'Tunde', 'Seun', 'Bayo', 'Femi', 'Kola',
        'Dayo', 'Adebayo', 'Chidi', 'Ikenna', 'Nnamdi', 'Uche', 'Jide', 'Rotimi',
        'Sola', 'Biodun', 'Gbenga', 'Yemi', 'Dele', 'Kunle', 'Segun', 'Wale',
        'Tobi', 'Dotun', 'Remi', 'Tunji', 'Kayode', 'Babajide', 'Olumide',
        'Adewale', 'Chinedu', 'Chibuike', 'Tochukwu', 'Ebuka', 'Ifeanyi',
        'Kingsley', 'Emmanuel', 'Victor', 'Felix', 'Daniel', 'Michael', 'David',
        'Samuel', 'Joseph', 'James', 'John', 'Peter', 'Paul', 'Goodluck',
        'Ovie', 'Eghosa', 'Osaze', 'Oghenekaro', 'Akpan', 'Effiong', 'Nsikan',
    ];

    private static array $femaleFirstNames = [
        'Ngozi', 'Chidinma', 'Adaeze', 'Amaka', 'Nkechi', 'Ify', 'Bimpe', 'Bukola',
        'Funke', 'Tosin', 'Ronke', 'Shade', 'Yetunde', 'Folake', 'Wunmi', 'Adeola',
        'Yewande', 'Abiodun', 'Chioma', 'Uchenna', 'Obiageli', 'Ifeoma', 'Nkiruka',
        'Chinwe', 'Ada', 'Ogechi', 'Nneka', 'Ebele', 'Chinyere', 'Blessing',
        'Grace', 'Favour', 'Joy', 'Patience', 'Peace', 'Hope', 'Faith', 'Mercy',
        'Precious', 'Gift', 'Chiamaka', 'Adaora', 'Oluwaseun', 'Temitope',
        'Abimbola', 'Omotola', 'Omowunmi', 'Olayinka', 'Titilayo',
        'Halima', 'Aisha', 'Fatima', 'Khadijah', 'Zainab', 'Maryam',
    ];

    private static array $surnames = [
        'Okafor', 'Adeyemi', 'Nwosu', 'Okonkwo', 'Adesanya', 'Eze', 'Adebayo',
        'Obi', 'Chukwu', 'Adeleke', 'Nwachukwu', 'Abubakar', 'Musa', 'Yusuf',
        'Ibrahim', 'Salami', 'Okoro', 'Igwe', 'Nwofor', 'Udeh', 'Effiong',
        'Idiong', 'Akpan', 'Bassey', 'Udo', 'Etuk', 'Okon', 'Nwankwo',
        'Kanu', 'Emenike', 'Oduya', 'Balogun', 'Afolabi', 'Abayomi',
        'Ogundele', 'Adekunle', 'Olawale', 'Adeniyi', 'Adenuga', 'Lawson',
        'Peterside', 'Mbanefo', 'Aliyu', 'Wike', 'Amaechi', 'Obaseki',
        'Ugwuanyi', 'Umahi', 'Soludo', 'Makinde', 'Mohammed', 'Abdullahi',
        'Sule', 'Yahaya', 'Tambuwal', 'Ribadu', 'Sanusi', 'Ekwueme',
    ];

    private static array $states = [
        'Lagos', 'Abuja', 'Kano', 'Rivers', 'Oyo', 'Kaduna', 'Enugu', 'Delta',
        'Anambra', 'Imo', 'Ogun', 'Kwara', 'Plateau', 'Cross River', 'Edo',
        'Benue', 'Abia', 'Akwa Ibom', 'Bauchi', 'Borno',
    ];

    private static array $cities = [
        'Lagos', 'Ikeja', 'Abuja', 'Kano', 'Ibadan', 'Port Harcourt', 'Benin City',
        'Maiduguri', 'Zaria', 'Aba', 'Jos', 'Ilorin', 'Oyo', 'Enugu', 'Abeokuta',
        'Onitsha', 'Warri', 'Sokoto', 'Calabar', 'Uyo', 'Kaduna', 'Owerri',
        'Akure', 'Bauchi', 'Minna', 'Katsina', 'Ado-Ekiti', 'Umuahia', 'Osogbo',
        'Yenagoa', 'Asaba', 'Awka', 'Lafia', 'Lokoja', 'Makurdi', 'Gombe', 'Yola',
    ];

    private static array $streets = [
        'Ahmadu Bello Way', 'Herbert Macaulay Street', 'Broad Street', 'Marina',
        'Awolowo Road', 'Adeola Odeku Street', 'Ozumba Mbadiwe Avenue',
        'Bode Thomas Street', 'Allen Avenue', 'Obafemi Awolowo Way',
        'Nnamdi Azikiwe Avenue', 'Yakubu Gowon Way', 'Adetokunbo Ademola Street',
        'Ribadu Road', 'Mobolaji Johnson Avenue', 'Akin Adesola Street',
        'Ligali Ayorinde Street', 'Karimu Kotun Street', 'Kofo Abayomi Street',
    ];

    private static array $areas = [
        'Victoria Island', 'Lekki Phase 1', 'Ikoyi', 'Surulere', 'Ikeja',
        'Yaba', 'Apapa', 'Gbagada', 'Maryland', 'Ojodu', 'Agege', 'Festac Town',
        'Ajah', 'Sangotedo', 'Magodo', 'Oregun', 'Isolo', 'Oshodi', 'Ketu',
    ];

    private static array $statusOptions = [
        ['status' => 'pending',  'account_status' => 'pending', 'verification_status' => 'not_verified', 'weight' => 30],
        ['status' => 'approved', 'account_status' => 'invited', 'verification_status' => 'not_verified', 'weight' => 40],
        ['status' => 'verified', 'account_status' => 'active',  'verification_status' => 'verified',     'weight' => 20],
        ['status' => 'rejected', 'account_status' => 'pending', 'verification_status' => 'not_verified', 'weight' => 10],
    ];

    private static array $rejectionReasons = [
        'Incomplete documentation provided.',
        'BVN details could not be verified.',
        'Identity card information is inconsistent.',
        'NIN does not match provided personal details.',
        'Suspicious activity detected during review.',
        'Date of birth mismatch with official records.',
        'Passport photograph does not meet requirements.',
    ];

    private static array $banks = [
        'GTBank', 'Access Bank', 'First Bank', 'Zenith Bank', 'UBA',
        'Fidelity Bank', 'Polaris Bank', 'Sterling Bank', 'Wema Bank', 'Keystone Bank',
    ];

    public function run(): void
    {
        $nin     = app(NinEncryptionService::class);
        $adminId = User::where('email', 'admin@josbiz.com')->value('id');

        $bulkStart = Carbon::create(2024, 7, 14);
        $bulkEnd   = Carbon::create(2026, 2, 28);

        $records = [];

        for ($i = 0; $i < self::TOTAL; $i++) {
            $date   = $i < 14
                ? Carbon::create(2026, 3, $i + 1)
                : Carbon::createFromTimestamp(mt_rand($bulkStart->timestamp, $bulkEnd->timestamp));

            $option    = $this->pickWeighted(self::$statusOptions);
            $status    = $option['status'];
            $gender    = $this->pick(['Male', 'Female']);
            $dob       = Carbon::now()->subYears(mt_rand(18, 65))->subDays(mt_rand(0, 364));
            $ninEnc    = $nin->encrypt($this->fakeNin());
            $bvnEnc    = $nin->encrypt($this->fakeBvn());
            $firstName = $gender === 'Male' ? $this->pick(self::$maleFirstNames) : $this->pick(self::$femaleFirstNames);
            $lastName  = $this->pick(self::$surnames);

            $approvedAt = null;
            $rejectedAt = null;
            $approvedBy = null;
            $rejectedBy = null;

            if (in_array($status, ['approved', 'verified'])) {
                $approvedBy = $adminId;
                $approvedAt = $date->copy()->addDays(mt_rand(1, 5))->toDateTimeString();
            } elseif ($status === 'rejected') {
                $rejectedBy = $adminId;
                $rejectedAt = $date->copy()->addDays(mt_rand(1, 3))->toDateTimeString();
            }

            $email = 'josbizhrintranet+individual' . ($i + 1) . '@gmail.com';

            $records[] = [
                'submission_type'         => $this->pick(['public_form', 'bulk_upload']),
                'status'                  => $status,
                'account_status'          => $option['account_status'],
                'verification_status'     => $option['verification_status'],
                'rejection_reason'        => $status === 'rejected' ? $this->pick(self::$rejectionReasons) : null,
                'title'                   => $gender === 'Male' ? $this->pick(['Mr', 'Dr', 'Prof']) : $this->pick(['Mrs', 'Miss', 'Dr']),
                'first_name'              => $firstName,
                'last_name'               => $lastName,
                'middle_name'             => $this->maybe(60, $this->pick(self::$surnames)),
                'gender'                  => $gender,
                'date_of_birth'           => $dob->toDateString(),
                'nationality'             => 'Nigerian',
                'phone_number'            => '080' . $this->digits(8),
                'confirm_phone_number'    => '080' . $this->digits(8),
                'foreign_number'          => null,
                'email_address'           => $email,
                'confirm_email_address'   => $email,
                'country'                 => 'Nigeria',
                'state'                   => $this->pick(self::$states),
                'city'                    => $this->pick(self::$cities),
                'chn'                     => $this->maybe(40, 'CHN' . $this->digits(9)),
                'house_address'           => $this->address(),
                'previous_address'        => $this->maybe(50, $this->address()),
                'bvn_encrypted'           => $bvnEnc['iv'] . ':' . $bvnEnc['ciphertext'] . ':' . $bvnEnc['tag'],
                'bank_name'               => $this->pick(self::$banks),
                'bank_account_number'     => $this->digits(10),
                'tin_or_pin'              => $this->maybe(50, $this->digits(10)),
                'bank_details_consent'    => 1,
                'identity_card_type'      => 'National Identity Number',
                'identity_card_number'    => null,
                'nin_number_encrypted'    => $ninEnc['iv'] . ':' . $ninEnc['ciphertext'] . ':' . $ninEnc['tag'],
                'identity_card_path'      => null,
                'scanned_signature_path'  => null,
                'passport_photograph_path'=> null,
                'nin_document_path'       => null,
                'consent_given'           => 1,
                'submitted_by'            => null,
                'approved_by'             => $approvedBy,
                'approved_at'             => $approvedAt,
                'rejected_by'             => $rejectedBy,
                'rejected_at'             => $rejectedAt,
                'created_at'              => $date->toDateTimeString(),
                'updated_at'              => $date->toDateTimeString(),
            ];
        }

        shuffle($records);

        foreach (array_chunk($records, self::BATCH_SIZE) as $chunk) {
            IndividualRegistration::insert($chunk);
        }
    }

    private function pick(array $items): mixed
    {
        return $items[array_rand($items)];
    }

    private function digits(int $count): string
    {
        $result = '';
        for ($i = 0; $i < $count; $i++) {
            $result .= mt_rand(0, 9);
        }

        return $result;
    }

    private function address(): string
    {
        return mt_rand(1, 120) . ' ' . $this->pick(self::$streets) . ', ' . $this->pick(self::$areas);
    }

    private function maybe(int $percent, mixed $value): mixed
    {
        return mt_rand(1, 100) <= $percent ? $value : null;
    }

    private function pickWeighted(array $options): array
    {
        $total      = array_sum(array_column($options, 'weight'));
        $rand       = mt_rand(1, $total);
        $cumulative = 0;

        foreach ($options as $option) {
            $cumulative += $option['weight'];
            if ($rand <= $cumulative) {
                return $option;
            }
        }

        return $options[array_key_last($options)];
    }

    private function fakeNin(): string
    {
        return sprintf('%011d', mt_rand(10000000000, 99999999999));
    }

    private function fakeBvn(): string
    {
        return sprintf('%011d', mt_rand(10000000000, 99999999999));
    }
}
