<?php
declare(strict_types=1);

namespace App\Core;

use App\Models\User;
use App\Services\SetupService;
use App\Services\TimezoneService;

final class Auth
{
    private static ?self $instance = null;
    private ?array $user = null;
    private ?array $tenant = null;
    /** @var string[] */
    private array $roles = [];

    private function __construct()
    {
        $userId = Session::get('user_id');
        $tenantId = Session::get('tenant_id');
        if ($userId) {
            $this->user = User::findActive((int) $userId);
            if (!$this->user) {
                $this->logout();
                return;
            }
            $this->roles = Session::get('roles') ?? [];
            if ($tenantId) {
                $this->tenant = User::tenantById((int) $userId, (int) $tenantId);
                if ($this->tenant) {
                    Session::set('tenant', $this->tenant);
                }
            }
            if (!$this->tenant) {
                $this->tenant = Session::get('tenant');
            }
            TimezoneService::apply(is_array($this->tenant) ? ($this->tenant['timezone'] ?? null) : null);
        }
    }

    public static function instance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function check(): bool
    {
        return $this->user !== null;
    }

    public function user(): ?array
    {
        return $this->user;
    }

    public function id(): ?int
    {
        return $this->user ? (int) $this->user['id'] : null;
    }

    public function tenant(): ?array
    {
        return $this->tenant;
    }

    public function tenantId(): ?int
    {
        return $this->tenant ? (int) $this->tenant['id'] : null;
    }

    /** @return string[] */
    public function roles(): array
    {
        return $this->roles;
    }

    public function hasRole(string ...$codes): bool
    {
        foreach ($codes as $code) {
            if (in_array($code, $this->roles, true)) {
                return true;
            }
        }
        return false;
    }

    public function canSee(string $module): bool
    {
        if (in_array($module, ['setup', 'users', 'roles', 'industry', 'marketing_connect', 'integrations', 'customer_portal'], true)) {
            return $this->hasRole('admin');
        }
        $level = $this->setupLevel($module);
        if ($level !== null) {
            return $level !== 'hide';
        }
        return $this->defaultCanSee($module);
    }

    public function canEdit(string $module): bool
    {
        if (!$this->canSee($module)) {
            return false;
        }
        if (in_array($module, ['setup', 'users', 'roles', 'industry', 'marketing_connect', 'integrations', 'customer_portal'], true)) {
            return $this->hasRole('admin');
        }
        $level = $this->setupLevel($module);
        if ($level !== null) {
            return $level === 'edit';
        }
        return $this->defaultCanSee($module);
    }

    /** @return array<string, list<string>> */
    public static function defaultModuleMap(): array
    {
        return [
            'dashboard' => ['salesperson', 'manager', 'admin', 'service', 'marketing'],
            'leads' => ['salesperson', 'manager'],
            'accounts' => ['salesperson', 'manager', 'service'],
            'contacts' => ['salesperson', 'manager', 'service'],
            'opportunities' => ['salesperson', 'manager'],
            'tasks' => ['salesperson', 'manager', 'service'],
            'calendar' => ['salesperson', 'manager', 'service'],
            'emails' => ['salesperson', 'manager'],
            'reports' => ['manager'],
            'assignment' => ['manager'],
            'users' => ['admin'],
            'roles' => ['admin'],
            'settings' => ['admin', 'salesperson', 'manager', 'service', 'marketing'],
            'integrations' => ['admin'],
            'campaigns' => ['marketing'],
            'lists' => ['marketing'],
            'templates' => ['marketing'],
            'unsubscribes' => ['marketing', 'admin'],
            'campaign_reports' => ['marketing', 'manager', 'salesperson'],
            'service_visits' => ['service', 'manager'],
            'quotes' => ['salesperson', 'manager'],
            'documents' => ['salesperson', 'manager'],
            'orders' => ['salesperson', 'manager'],
            'contracts' => ['salesperson', 'manager'],
            'invoices' => ['salesperson', 'manager'],
            'approvals' => ['manager'],
            'renewals' => ['salesperson', 'manager'],
            'service_jobs' => ['service', 'manager'],
            'equipment' => ['service', 'manager'],
            'industry' => ['admin'],
            'marketing_connect' => ['admin'],
            'customer_portal' => ['admin'],
            'setup' => ['admin'],
        ];
    }

    private function defaultCanSee(string $module): bool
    {
        $allowed = self::defaultModuleMap()[$module] ?? [];
        return $this->hasRole(...$allowed);
    }

    private function setupLevel(string $module): ?string
    {
        if ($this->roles === [] || !$this->tenantId()) {
            return null;
        }
        try {
            $rank = ['hide' => 0, 'view' => 1, 'edit' => 2];
            $best = -1;
            foreach ($this->roles as $role) {
                $best = max($best, $rank[SetupService::permission($role, $module)] ?? -1);
            }
            if ($best < 0) {
                return null;
            }
            return array_search($best, $rank, true) ?: 'hide';
        } catch (\Throwable $e) {
            return null;
        }
    }

    public function login(array $user, array $tenant, array $roles): void
    {
        Session::regenerate();
        Session::set('user_id', (int) $user['id']);
        Session::set('tenant_id', (int) $tenant['id']);
        Session::set('tenant', $tenant);
        Session::set('roles', $roles);
        Csrf::token();
        $this->user = $user;
        $this->tenant = $tenant;
        $this->roles = $roles;
        TimezoneService::apply($tenant['timezone'] ?? null);
        self::$instance = $this;
    }

    public function setTenant(array $tenant, array $roles): void
    {
        Session::set('tenant_id', (int) $tenant['id']);
        Session::set('tenant', $tenant);
        Session::set('roles', $roles);
        $this->tenant = $tenant;
        $this->roles = $roles;
        TimezoneService::apply($tenant['timezone'] ?? null);
    }

    public function logout(): void
    {
        Session::destroy();
        $this->user = null;
        $this->tenant = null;
        $this->roles = [];
        TimezoneService::apply(null);
        self::$instance = new self();
    }
}
