<?php

namespace Database\Seeders;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class MembersTableSeeder extends Seeder
{
    // tune for your machine
    private int $OUTER_CHUNK = 5000;

    // MySQL placeholder ceiling is ~65k; stay well under it
    private int $MAX_PLACEHOLDERS = 60000;

    // hard caps to keep packets small and memory stable
    private int $MEMBERS_HARD_CAP = 1200;   // max rows per members INSERT
    private int $ADDR_HARD_CAP    = 2000;   // max rows per addresses INSERT

    public function run(): void
    {
        DB::disableQueryLog();

        Schema::disableForeignKeyConstraints();
        DB::table('members_addresses')->truncate();
        DB::table('members')->truncate();
        Schema::enableForeignKeyConstraints();

        $total = (int) env('SEED_MEMBERS', 100000);
        $outer = (int) env('SEED_MEMBERS_CHUNK', $this->OUTER_CHUNK);

        // one bcrypt for everyone (bcrypt is expensive)
        $passwordHash = password_hash('Password123', PASSWORD_BCRYPT);
        $now = now();

        for ($offset = 0; $offset < $total; $offset += $outer) {
            $count = min($outer, $total - $offset);

            // ---------- build this outer batch of members ----------
            $members = [];
            for ($i = 0; $i < $count; $i++) {
                $n      = $offset + $i + 1;
                $email  = "user{$n}@example.test";
                $phone  = '07' . str_pad((string) mt_rand(0, 999_999_999), 9, '0', STR_PAD_LEFT);

                $members[] = [
                    'unio_internal_id'             => null,
                    'is_external'                  => 0,
                    'is_activated'                 => 1,
                    'email'                        => $email,
                    'phone_prefix'                 => '+44',
                    'contact_no'                   => $phone,
                    'password'                     => $passwordHash,
                    'verification_code'            => null,
                    'verification_code_expiry'     => null,
                    'prescription_reminder_email'  => 0,
                    'prescription_reminder_sms'    => 0,
                    'dob'                          => null,
                    'gender'                       => null,
                    'height'                       => 0,
                    'first_login'                  => $now,
                    'referrer_member_id'           => null,
                    'account_verified'             => $now,
                    'last_login'                   => $now,
                    'last_failed_login'            => $now,
                    'created_at'                   => $now,
                    'updated_at'                   => $now,
                ];
            }

            // columns per members row (for placeholder math)
            $memberCols = count($members ? array_keys($members[0]) : []);
            // rows-per-insert bounded by placeholder limit and hard cap
            $rowsPerMembersInsert = $memberCols > 0
                ? min($this->MEMBERS_HARD_CAP, max(1, intdiv($this->MAX_PLACEHOLDERS, $memberCols)))
                : $this->MEMBERS_HARD_CAP;

            DB::transaction(function () use ($members, $rowsPerMembersInsert, $now) {

                // Split the big members array into safe sub-batches
                for ($mOff = 0, $mTot = count($members); $mOff < $mTot; $mOff += $rowsPerMembersInsert) {
                    $slice = array_slice($members, $mOff, $rowsPerMembersInsert);

                    // parent insert (safe size)
                    DB::table('members')->insert($slice);

                    // first auto-increment ID for THIS sub-insert
                    $firstId  = (int) DB::getPdo()->lastInsertId();
                    $inserted = count($slice);

                    // ---------- build the two addresses per inserted member ----------
                    $addresses = [];
                    for ($i = 0; $i < $inserted; $i++) {
                        $memberId = $firstId + $i;

                        // Billing
                        $bf = fake()->firstName();
                        $bl = fake()->lastName();
                        $bp = fake()->postcode();
                        $addresses[] = [
                            'member_id'      => $memberId,
                            'type'           => 'billing',
                            'firstname'      => $bf,
                            'middle_name'    => '',
                            'lastname'       => $bl,
                            'address1'       => fake()->streetAddress(),
                            'address2'       => '',
                            'city'           => fake()->city(),
                            'state'          => '',
                            'country'        => 'GB',
                            'postcode'       => $bp,
                            'title'          => null,
                        ];

                        // Shipping
                        $sf = fake()->firstName();
                        $sl = fake()->lastName();
                        $sp = fake()->postcode();
                        $addresses[] = [
                            'member_id'      => $memberId,
                            'type'           => 'shipping',
                            'firstname'      => $sf,
                            'middle_name'    => '',
                            'lastname'       => $sl,
                            'address1'       => fake()->streetAddress(),
                            'address2'       => '',
                            'city'           => fake()->city(),
                            'state'          => '',
                            'country'        => 'GB',
                            'postcode'       => $sp,
                            'title'          => null,
                        ];
                    }

                    // columns per address row (for placeholder math)
                    $addrCols = count($addresses ? array_keys($addresses[0]) : []);
                    $rowsPerAddrInsert = $addrCols > 0
                        ? min($this->ADDR_HARD_CAP, max(1, intdiv($this->MAX_PLACEHOLDERS, $addrCols)))
                        : $this->ADDR_HARD_CAP;

                    // child inserts in safe chunks
                    for ($aOff = 0, $aTot = count($addresses); $aOff < $aTot; $aOff += $rowsPerAddrInsert) {
                        DB::table('members_addresses')->insert(array_slice($addresses, $aOff, $rowsPerAddrInsert));
                    }
                }
            });
        }
    }

}
