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