<?php

declare(strict_types=1);

namespace Database\Seeders;

use App\Enums\HouseholdRole;
use App\Enums\MembershipStatus;
use App\Enums\TransferStatus;
use App\Models\Branch;
use App\Models\Group;
use App\Models\Household;
use App\Models\Member;
use App\Models\MemberNote;
use App\Models\MemberStatusHistory;
use App\Models\MemberTransfer;
use App\Models\Region;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Hash;

class DevSeeder extends Seeder
{
    public function run(): void
    {
        fake()->seed(20260710);

        $regions = Region::factory()->count(3)->create();

        $branches = $regions->flatMap(
            fn (Region $region) => Branch::factory()->count(4)->create(['region_id' => $region->id])
        );

        $this->createUsers($regions->first(), $branches->first());

        $membersByBranch = $branches->mapWithKeys(function (Branch $branch) {
            $count = random_int(150, 200);

            return [$branch->id => Member::factory()->count($count)->create(['branch_id' => $branch->id])];
        });

        $this->createHouseholds($branches, $membersByBranch);
        $this->createGroups($branches, $membersByBranch);
        $this->createNotesAndHistory($membersByBranch);
        $this->createPendingTransfers($branches, $membersByBranch);
    }

    private function createUsers(Region $region, Branch $branch): void
    {
        User::factory()->superAdmin()->create([
            'name' => 'Super Admin',
            'email' => 'admin@example.com',
            'password' => Hash::make('password'),
        ]);

        User::factory()->regionAdmin()->create([
            'name' => 'Region Admin',
            'email' => 'region.admin@example.com',
            'region_id' => $region->id,
            'password' => Hash::make('password'),
        ]);

        User::factory()->branchAdmin()->create([
            'name' => 'Branch Admin',
            'email' => 'branch.admin@example.com',
            'branch_id' => $branch->id,
            'password' => Hash::make('password'),
        ]);

        User::factory()->dataClerk()->create([
            'name' => 'Data Clerk',
            'email' => 'data.clerk@example.com',
            'branch_id' => $branch->id,
            'password' => Hash::make('password'),
        ]);

        User::factory()->branchViewer()->create([
            'name' => 'Viewer',
            'email' => 'viewer@example.com',
            'branch_id' => $branch->id,
            'password' => Hash::make('password'),
        ]);
    }

    /**
     * @param  Collection<int, Branch>  $branches
     * @param  Collection<int, Collection<int, Member>>  $membersByBranch
     */
    private function createHouseholds($branches, $membersByBranch): void
    {
        $householdsPerBranch = intdiv(200, $branches->count());

        foreach ($branches as $branch) {
            $availableMembers = $membersByBranch[$branch->id]->shuffle()->values();
            $cursor = 0;

            for ($i = 0; $i < $householdsPerBranch; $i++) {
                $size = random_int(2, 6);
                $members = $availableMembers->slice($cursor, $size);
                $cursor += $size;

                if ($members->count() < 2) {
                    break;
                }

                $household = Household::factory()->create(['branch_id' => $branch->id]);

                $head = $members->first();
                $roles = [HouseholdRole::Spouse, HouseholdRole::Child, HouseholdRole::Other];

                foreach ($members as $index => $member) {
                    $member->update([
                        'household_id' => $household->id,
                        'household_role' => $index === 0 ? HouseholdRole::Head : fake()->randomElement($roles),
                    ]);
                }

                $household->update(['head_member_id' => $head->id]);
            }
        }
    }

    /**
     * @param  Collection<int, Branch>  $branches
     * @param  Collection<int, Collection<int, Member>>  $membersByBranch
     */
    private function createGroups($branches, $membersByBranch): void
    {
        $orgWideCount = 4;
        $perBranchCount = intdiv(40 - $orgWideCount, $branches->count());

        Group::factory()->organizationWide()->count($orgWideCount)->create()->each(function (Group $group) use ($membersByBranch) {
            $pool = $membersByBranch->flatten(1)->shuffle()->take(random_int(10, 50));
            $this->attachMembers($group, $pool);
        });

        foreach ($branches as $branch) {
            Group::factory()->count($perBranchCount)->create(['branch_id' => $branch->id])
                ->each(function (Group $group) use ($membersByBranch, $branch) {
                    $pool = $membersByBranch[$branch->id]->shuffle()->take(random_int(10, 50));
                    $this->attachMembers($group, $pool);
                });
        }
    }

    /**
     * @param  Collection<int, Member>  $members
     */
    private function attachMembers(Group $group, $members): void
    {
        $attachData = $members->mapWithKeys(fn (Member $member, int $index) => [
            $member->id => [
                'role_in_group' => $index === 0 ? 'leader' : 'member',
                'joined_at' => fake()->dateTimeBetween('-3 years', 'now'),
            ],
        ]);

        $group->members()->attach($attachData->all());
    }

    /**
     * @param  Collection<int, Collection<int, Member>>  $membersByBranch
     */
    private function createNotesAndHistory($membersByBranch): void
    {
        $allMembers = $membersByBranch->flatten(1)->values();

        $allMembers->random(50)->each(fn (Member $member) => MemberNote::factory()->create([
            'member_id' => $member->id,
        ]));

        $statuses = MembershipStatus::cases();

        $allMembers->random(30)->each(function (Member $member) use ($statuses) {
            $from = fake()->randomElement($statuses);
            MemberStatusHistory::factory()->create([
                'member_id' => $member->id,
                'from_status' => $from,
                'to_status' => $member->membership_status,
            ]);
        });
    }

    /**
     * @param  Collection<int, Branch>  $branches
     * @param  Collection<int, Collection<int, Member>>  $membersByBranch
     */
    private function createPendingTransfers($branches, $membersByBranch): void
    {
        $requester = User::where('email', 'branch.admin@example.com')->firstOrFail();

        for ($i = 0; $i < 15; $i++) {
            $fromBranch = $branches->random();
            $toBranch = $branches->where('id', '!=', $fromBranch->id)->random();
            $member = $membersByBranch[$fromBranch->id]->random();

            MemberTransfer::factory()->create([
                'member_id' => $member->id,
                'from_branch_id' => $fromBranch->id,
                'to_branch_id' => $toBranch->id,
                'status' => TransferStatus::Pending,
                'requested_by' => $requester->id,
            ]);
        }
    }
}
