<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * There is no `player` role. A player given the `parent` role gets empty lists
 * everywhere, because PermissionService::getChildParticipantIds() resolves the
 * user's scope only through a Guardian row — and an adult member playing for
 * himself has no guardian.
 *
 * Delivered as a migration, not a seeder: db:seed only runs when
 * RUN_SEED_ON_FIRST_DEPLOY is true (docker/entrypoint.sh), so any client
 * deployed outside the one-click template would never receive it. Same pattern
 * and same reasoning as 2026_09_01_000001_add_branches_view_all_permission.
 *
 * Idempotent: every insert checks for its row first.
 */
return new class extends Migration
{
    /** name => [module, action, description, description_ar] */
    private const PERMISSIONS = [
        'portal.access' => ['portal', 'access', 'Sign in to the member portal', 'الدخول إلى بوابة الأعضاء'],
        'portal.pay' => ['portal', 'pay', 'Submit a payment or a payment proof', 'تسجيل دفعة أو إرسال إثبات تحويل'],
        'portal.documents' => ['portal', 'documents', 'Upload and renew own documents', 'رفع وتجديد المستندات'],
        'portal.requests' => ['portal', 'requests', 'Open a service request or an excuse', 'تقديم طلب أو عذر'],
        'payments.approve_proof' => ['payments', 'approve_proof', 'Approve a transfer proof into a real payment', 'اعتماد إثبات التحويل وتحويله إلى دفعة'],
        'attendance.scan' => ['attendance', 'scan', 'Scan a member pass to mark attendance', 'مسح بطاقة العضو لتسجيل الحضور'],
        'users.merge' => ['users', 'merge', 'Merge duplicate member accounts', 'دمج الحسابات المكررة'],
    ];

    /**
     * Everything the `parent` role already holds, which a player needs for
     * himself. The scope stays `own_children` rather than `own`: `own` means
     * `created_by = user.id`, which is about authorship, not membership — a
     * player did not create his own attendance records. GuardianResolver makes
     * `own_children` mean "the participants this account speaks for", which for
     * a player is himself.
     */
    private const PLAYER_SCOPED = [
        'attendance.list', 'evaluations.list', 'excuses.submit',
        'invoices.list', 'invoices.view', 'participants.show', 'participants.view',
        'payments.list', 'schedules.view', 'wallets.view',
        'portal.access', 'portal.pay', 'portal.documents', 'portal.requests',
    ];

    private const PLAYER_ACADEMY = ['dashboard.view', 'programs.list'];

    /** The member-facing permissions the existing parent role also gains. */
    private const PARENT_ADDITIONS = ['portal.access', 'portal.pay', 'portal.documents', 'portal.requests'];

    private const STAFF_APPROVALS = ['payments.approve_proof', 'users.merge'];

    private const STAFF_SCAN = ['attendance.scan'];

    public function up(): void
    {
        if (! Schema::hasTable('permissions') || ! Schema::hasTable('roles') || ! Schema::hasTable('permission_role')) {
            return;
        }

        $ids = [];

        foreach (self::PERMISSIONS as $name => [$module, $action, $description, $descriptionAr]) {
            $ids[$name] = DB::table('permissions')->where('name', $name)->value('id')
                ?: DB::table('permissions')->insertGetId([
                    'name' => $name,
                    'module' => $module,
                    'action' => $action,
                    'description' => $description,
                    'description_ar' => $descriptionAr,
                    'created_at' => now(),
                ]);
        }

        // Roles are per-academy rows, so this creates one player role per tenant.
        foreach (DB::table('academies')->pluck('id') as $academyId) {
            $playerId = DB::table('roles')
                ->where('academy_id', $academyId)
                ->where('slug', 'player')
                ->value('id');

            if (! $playerId) {
                $playerId = DB::table('roles')->insertGetId([
                    'academy_id' => $academyId,
                    'name' => 'Player',
                    'name_ar' => 'لاعب',
                    'slug' => 'player',
                    // Same level as parent: both are member-facing, neither
                    // outranks the other.
                    'level' => 5,
                    'description' => 'A member who plays for himself',
                    'is_system' => true,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }

            $this->grant($playerId, self::PLAYER_SCOPED, 'own_children');
            $this->grant($playerId, self::PLAYER_ACADEMY, 'academy');

            $parentId = DB::table('roles')
                ->where('academy_id', $academyId)
                ->where('slug', 'parent')
                ->value('id');

            if ($parentId) {
                $this->grant($parentId, self::PARENT_ADDITIONS, 'own_children');
            }

            foreach (['academy_owner', 'academy_admin', 'accountant'] as $slug) {
                $roleId = DB::table('roles')->where('academy_id', $academyId)->where('slug', $slug)->value('id');

                if ($roleId) {
                    $this->grant($roleId, self::STAFF_APPROVALS, 'academy');
                }
            }

            foreach (['academy_owner', 'academy_admin', 'branch_manager', 'receptionist', 'head_trainer', 'trainer'] as $slug) {
                $roleId = DB::table('roles')->where('academy_id', $academyId)->where('slug', $slug)->value('id');

                if ($roleId) {
                    $this->grant($roleId, self::STAFF_SCAN, 'branch');
                }
            }
        }

        // super_admin rows are not academy-scoped in the same way; grant everything.
        foreach (DB::table('roles')->where('slug', 'super_admin')->pluck('id') as $roleId) {
            $this->grant($roleId, array_keys(self::PERMISSIONS), 'all');
        }
    }

    private function grant(int $roleId, array $permissionNames, string $scope): void
    {
        foreach ($permissionNames as $name) {
            $permissionId = DB::table('permissions')->where('name', $name)->value('id');

            if (! $permissionId) {
                continue;
            }

            $exists = DB::table('permission_role')
                ->where('role_id', $roleId)
                ->where('permission_id', $permissionId)
                ->exists();

            if (! $exists) {
                DB::table('permission_role')->insert([
                    'role_id' => $roleId,
                    'permission_id' => $permissionId,
                    'scope' => $scope,
                    'created_at' => now(),
                ]);
            }
        }
    }

    public function down(): void
    {
        if (! Schema::hasTable('permissions')) {
            return;
        }

        $ids = DB::table('permissions')->whereIn('name', array_keys(self::PERMISSIONS))->pluck('id');

        if (Schema::hasTable('permission_role')) {
            DB::table('permission_role')->whereIn('permission_id', $ids)->delete();
        }

        DB::table('permissions')->whereIn('id', $ids)->delete();

        if (Schema::hasTable('roles')) {
            $playerIds = DB::table('roles')->where('slug', 'player')->pluck('id');
            DB::table('permission_role')->whereIn('role_id', $playerIds)->delete();
            DB::table('roles')->whereIn('id', $playerIds)->delete();
        }
    }
};
