<?php

namespace App\Services;

use App\Models\Department;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class UserDepartmentResolver
{
    /**
     * Resolve the department_id for a given user based on their roles.
     *
     * Returns null if no change should be applied.
     */
    public function resolveDepartmentIdForUser(User $user): ?int
    {
        if (! $user->dealer_id) {
            return null;
        }

        return $this->resolveDepartmentIdForDealer($user, (int) $user->dealer_id);
    }

    /**
     * Apply resolved department to the user if it differs (UI / request context).
     */
    public function enforce(User $user): void
    {
        if (! $user->dealer_id) {
            return;
        }

        $this->enforceForDealer($user, (int) $user->dealer_id);
    }

    /**
     * Apply resolved department for a specific dealer, independent of request context.
     */
    public function enforceForDealer(User $user, int $dealerId): void
    {
        if ($dealerId <= 0) {
            return;
        }

        $departmentId = $this->resolveDepartmentIdForDealer($user, $dealerId);

        if ($departmentId && $user->department_id !== $departmentId) {
            $user->department_id = $departmentId;
            $user->save();
        }
    }

    /**
     * Internal helper: resolve department for a given dealer, using Spatie team-aware roles.
     */
    protected function resolveDepartmentIdForDealer(User $user, int $dealerId): ?int
    {
        // Platform-level users (no dealer) are ignored.
        if (! $dealerId) {
            return null;
        }

        $roleDepartmentMap = config('mtt_tenant_baseline.role_department_map', []);
        $rolePriority = config('mtt_tenant_baseline.role_priority', []);
        $platformOnlyDepartments = config('mtt_tenant_baseline.platform_only_departments', []);

        $tableNames = config('permission.table_names');
        $rolesTable = $tableNames['roles'] ?? 'roles';
        $modelHasRolesTable = $tableNames['model_has_roles'] ?? 'model_has_roles';

        // Fetch role names for this user scoped to the specific dealer via Spatie's teams (dealer_id on roles).
        $roleNames = DB::table($modelHasRolesTable.' as mhr')
            ->join($rolesTable.' as roles', 'roles.id', '=', 'mhr.role_id')
            ->where('mhr.model_type', User::class)
            ->where('mhr.model_id', $user->getKey())
            ->where('roles.dealer_id', $dealerId)
            ->pluck('roles.name')
            ->all();

        if ($roleNames === []) {
            return null;
        }

        $candidates = [];
        foreach ($roleNames as $roleName) {
            if (!isset($roleDepartmentMap[$roleName])) {
                continue;
            }

            $departmentName = $roleDepartmentMap[$roleName];

            // Guard: platform-only departments (e.g. System Administration) are never assigned to dealer users.
            if (in_array($departmentName, $platformOnlyDepartments, true)) {
                continue;
            }

            $priority = $rolePriority[$roleName] ?? 0;
            $candidates[] = [
                'role' => $roleName,
                'department' => $departmentName,
                'priority' => $priority,
            ];
        }

        if ($candidates === []) {
            return null;
        }

        // Pick highest priority candidate.
        usort($candidates, static function (array $a, array $b): int {
            return $b['priority'] <=> $a['priority'];
        });

        $targetDepartmentName = $candidates[0]['department'];

        $departmentId = Department::withoutGlobalScopes()
            ->where('dealer_id', $dealerId)
            ->where('name', $targetDepartmentName)
            ->value('id');

        return $departmentId ?: null;
    }

}

