<?php

declare(strict_types=1);

class Auth
{
    private static function usersFile(): string
    {
        $config = app_config();
        $storage = (string)($config['storage_path'] ?? app_path('storage'));
        ensure_dir($storage);
        return rtrim($storage, '/\\') . '/users.json';
    }

    public static function permissionsCatalog(): array
    {
        return [
            'dashboard'      => 'داشبورد',
            'unmapped'       => 'کالاهای مپ نشده',
            'smart-map'      => 'تنظیم مپ قیمت هوشمند',
            'smart-review'   => 'بررسی و اعمال قیمت هوشمند',
            'text-list'      => 'تولید لیست متنی',
            'export-all'     => 'استخراج همه فایل‌ها',
            'rayan-requests' => 'ثبت فاکتور',
            'rayan-form-settings' => 'تنظیمات فرم دستی',
            'rayan-inquiry'  => 'استعلام بازنشسته تامین اجتماعی',
            'hide-request-installment-amount' => 'مخفی کردن مبلغ خرید اعتباری / با سود در ثبت فاکتور',
            'hide-request-sales-amount' => 'مخفی کردن مبلغ فاکتور فروش حسابان‌وب در ثبت فاکتور',
            'hide-request-purchase-amount' => 'مخفی کردن مبلغ خرید حسابان‌وب در ثبت فاکتور',
            'logs'           => 'مشاهده لاگ‌ها',
            'download'       => 'دانلود فایل‌های خروجی و runtime',
            'users'          => 'مدیریت کاربران و سطح دسترسی',
        ];
    }

    public static function menuItems(): array
    {
        return [
            ['page' => 'dashboard', 'label' => 'داشبورد', 'section' => ''],
            ['page' => 'unmapped', 'label' => 'کالاهای مپ نشده', 'section' => 'قیمت و کالا'],
            ['page' => 'smart-map', 'label' => 'تنظیم مپ قیمت هوشمند', 'section' => 'قیمت و کالا'],
            ['page' => 'smart-review', 'label' => 'بررسی قیمت هوشمند', 'section' => 'قیمت و کالا'],
            ['page' => 'text-list', 'label' => 'تولید لیست متنی', 'section' => 'قیمت و کالا'],
            ['page' => 'export-all', 'label' => 'استخراج همه فایل‌ها', 'section' => 'قیمت و کالا'],
            ['page' => 'rayan-requests', 'label' => 'ثبت فاکتور', 'section' => 'فرم‌ها'],
            ['page' => 'rayan-form-settings', 'label' => 'تنظیمات فرم', 'section' => 'فرم‌ها'],
            ['page' => 'rayan-inquiry', 'label' => 'استعلام بازنشسته تامین اجتماعی', 'section' => 'فرم‌ها'],
            ['page' => 'users', 'label' => 'کاربران و دسترسی‌ها', 'section' => 'مدیریت'],
            ['page' => 'logs', 'label' => 'لاگ‌ها', 'section' => 'مدیریت'],
        ];
    }

    public static function pagePermission(string $page): ?string
    {
        return match ($page) {
            'dashboard' => 'dashboard',
            'unmapped' => 'unmapped',
            'smart-map' => 'smart-map',
            'smart-review' => 'smart-review',
            'text-list' => 'text-list',
            'export-all' => 'export-all',
            'rayan-requests' => 'rayan-requests',
            'rayan-form-settings' => 'rayan-form-settings',
            'rayan-inquiry' => 'rayan-inquiry',
            'logs' => 'logs',
            'download' => 'download',
            'users' => 'users',
            default => null,
        };
    }

    public static function taskPermission(string $task): ?string
    {
        return match ($task) {
            'run_exports' => 'export-all',
            'run_unmapped' => 'unmapped',
            'generate_template', 'upload_map' => 'smart-map',
            'generate_review', 'upload_review', 'apply_saved_review' => 'smart-review',
            'build_text' => 'text-list',
            'rayan_process', 'rayan_retry', 'rayan_submit_otp', 'rayan_manual_create' => 'rayan-requests',
            'rayan_inquiry' => 'rayan-inquiry',
            default => null,
        };
    }

    private static function normalizePermissions(array $permissions): array
    {
        $catalog = self::permissionsCatalog();
        $clean = [];
        foreach ($permissions as $permission) {
            $permission = trim((string)$permission);
            if ($permission === '*') {
                return ['*'];
            }
            if (isset($catalog[$permission])) {
                $clean[$permission] = $permission;
            }
        }
        if (empty($clean)) {
            $clean['dashboard'] = 'dashboard';
        }
        return array_values($clean);
    }

    private static function defaultAdminUser(): array
    {
        $config = app_config();
        $username = trim((string)($config['admin_username'] ?? 'admin')) ?: 'admin';
        $password = (string)($config['admin_password'] ?? '123456');
        return [
            'username' => $username,
            'display_name' => 'مدیر اصلی',
            'role' => 'admin',
            'permissions' => ['*'],
            'password_hash' => password_hash($password, PASSWORD_DEFAULT),
            'password_plain' => $password,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s'),
            'is_active' => true,
            'formafzar_creator_ids' => [],
            'formafzar_creator_id' => '',
        ];
    }

    private static function loadUsers(): array
    {
        $file = self::usersFile();
        if (!is_file($file)) {
            $admin = self::defaultAdminUser();
            self::saveUsers([$admin['username'] => $admin]);
        }

        $raw = is_file($file) ? (string)file_get_contents($file) : '';
        $data = json_decode($raw, true);
        if (!is_array($data)) {
            $admin = self::defaultAdminUser();
            self::saveUsers([$admin['username'] => $admin]);
            return [$admin['username'] => $admin];
        }

        $users = [];
        foreach ($data as $key => $user) {
            if (!is_array($user)) {
                continue;
            }
            $username = trim((string)($user['username'] ?? $key));
            if ($username === '') {
                continue;
            }
            $user['username'] = $username;
            $user['display_name'] = trim((string)($user['display_name'] ?? $username));
            $user['role'] = (string)($user['role'] ?? 'user');
            $user['permissions'] = self::normalizePermissions((array)($user['permissions'] ?? ['dashboard']));
            $rawCreatorIds = $user['formafzar_creator_ids'] ?? ($user['formafzar_creator_id'] ?? []);
            if (!is_array($rawCreatorIds)) {
                $rawCreatorIds = preg_split('/[\r\n,،;؛]+/u', (string)$rawCreatorIds) ?: [];
            }
            $creatorIds = [];
            foreach ($rawCreatorIds as $creatorId) {
                $creatorId = trim((string)$creatorId);
                if ($creatorId !== '') {
                    $creatorIds[$creatorId] = $creatorId;
                }
            }
            $user['formafzar_creator_ids'] = array_values($creatorIds);
            $user['formafzar_creator_id'] = implode("\n", $user['formafzar_creator_ids']);
            $user['is_active'] = array_key_exists('is_active', $user) ? (bool)$user['is_active'] : true;
            $users[$username] = $user;
        }

        if (!$users) {
            $admin = self::defaultAdminUser();
            self::saveUsers([$admin['username'] => $admin]);
            return [$admin['username'] => $admin];
        }

        return $users;
    }

    private static function saveUsers(array $users): void
    {
        $file = self::usersFile();
        ensure_dir(dirname($file));
        file_put_contents(
            $file,
            json_encode($users, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
        );
    }

    public static function listUsers(): array
    {
        $users = self::loadUsers();
        uasort($users, static function (array $a, array $b): int {
            if (($a['role'] ?? '') === ($b['role'] ?? '')) {
                return strcmp((string)($a['username'] ?? ''), (string)($b['username'] ?? ''));
            }
            return ((string)($a['role'] ?? '') === 'admin') ? -1 : 1;
        });
        return array_values($users);
    }

    public static function attempt(string $username, string $password): bool
    {
        $username = trim($username);
        $users = self::loadUsers();
        $user = $users[$username] ?? null;

        if (is_array($user) && (bool)($user['is_active'] ?? true)) {
            $hash = (string)($user['password_hash'] ?? '');
            if ($hash !== '' && password_verify($password, $hash)) {
                $_SESSION['auth_user'] = [
                    'username' => $username,
                    'display_name' => (string)($user['display_name'] ?? $username),
                    'role' => (string)($user['role'] ?? 'user'),
                    'permissions' => self::normalizePermissions((array)($user['permissions'] ?? [])),
                    'logged_in_at' => date('Y-m-d H:i:s'),
                ];
                log_app("LOGIN success user={$username}");
                return true;
            }
        }

        // سازگاری با نسخه‌های قبلی: اگر users.json هنوز ساخته نشده یا admin قدیمی استفاده می‌شود.
        $config = app_config();
        if ($username === (string)($config['admin_username'] ?? '') && $password === (string)($config['admin_password'] ?? '')) {
            $admin = self::defaultAdminUser();
            $users[$admin['username']] = $admin;
            self::saveUsers($users);
            $_SESSION['auth_user'] = [
                'username' => $admin['username'],
                'display_name' => $admin['display_name'],
                'role' => 'admin',
                'permissions' => ['*'],
                'logged_in_at' => date('Y-m-d H:i:s'),
            ];
            log_app("LOGIN legacy admin success user={$username}");
            return true;
        }

        log_app("LOGIN failed user={$username}");
        return false;
    }

    public static function check(): bool
    {
        return !empty($_SESSION['auth_user']);
    }

    public static function requireLogin(): void
    {
        if (!self::check()) {
            set_flash('error', 'ابتدا وارد پنل شوید.');
            redirect('login');
        }
    }

    public static function user(): ?array
    {
        return $_SESSION['auth_user'] ?? null;
    }

    public static function isAdmin(?array $user = null): bool
    {
        $user = $user ?? self::user();
        if (!$user) {
            return false;
        }
        if ((string)($user['role'] ?? '') === 'admin') {
            return true;
        }
        return in_array('*', (array)($user['permissions'] ?? []), true);
    }

    public static function can(string $permission): bool
    {
        if (!self::check()) {
            return false;
        }
        $user = self::user() ?? [];
        if (self::isAdmin($user)) {
            return true;
        }
        if ($permission === 'dashboard') {
            return true;
        }
        return in_array($permission, (array)($user['permissions'] ?? []), true);
    }

    public static function hasAssignedPermission(string $permission): bool
    {
        if (!self::check()) {
            return false;
        }
        $user = self::user() ?? [];
        $permissions = (array)($user['permissions'] ?? []);
        return in_array($permission, $permissions, true);
    }


    public static function requirePermission(string $permission): void
    {
        if (!self::can($permission)) {
            http_response_code(403);
            set_flash('error', 'شما به این بخش دسترسی ندارید.');
            redirect('dashboard');
        }
    }

    public static function requirePage(string $page): void
    {
        $permission = self::pagePermission($page);
        if ($permission !== null) {
            self::requirePermission($permission);
        }
    }

    public static function requireTask(string $task): void
    {
        $permission = self::taskPermission($task);
        if ($permission === null) {
            throw new RuntimeException('عملیات درخواستی معتبر نیست.');
        }
        self::requirePermission($permission);
    }

    public static function filterCards(array $cards): array
    {
        return array_values(array_filter($cards, static function (array $card): bool {
            $page = (string)($card['page'] ?? '');
            $permission = self::pagePermission($page);
            return $permission === null || self::can($permission);
        }));
    }

    private static function validateUsername(string $username): void
    {
        if (!preg_match('/^[A-Za-z0-9_.@-]{3,80}$/', $username)) {
            throw new RuntimeException('نام کاربری باید حداقل ۳ کاراکتر و فقط شامل حروف انگلیسی، عدد، نقطه، خط تیره، آندرلاین یا @ باشد.');
        }
    }

    private static function normalizeCreatorIdsInput($value): array
    {
        if (is_array($value)) {
            $parts = $value;
        } else {
            $parts = preg_split('/[\r\n,،;؛]+/u', (string)$value) ?: [];
        }
        $clean = [];
        foreach ($parts as $part) {
            $part = trim((string)$part);
            if ($part !== '') {
                $clean[$part] = $part;
            }
        }
        return array_values($clean);
    }

    public static function createUser(array $data): array
    {
        self::requirePermission('users');
        $users = self::loadUsers();
        $username = trim((string)($data['username'] ?? ''));
        self::validateUsername($username);
        if (isset($users[$username])) {
            throw new RuntimeException('این نام کاربری قبلاً ساخته شده است.');
        }
        $password = (string)($data['password'] ?? '');
        if (mb_strlen($password) < 6) {
            throw new RuntimeException('رمز عبور باید حداقل ۶ کاراکتر باشد.');
        }
        $role = ((string)($data['role'] ?? 'user') === 'admin') ? 'admin' : 'user';
        $permissions = $role === 'admin' ? ['*'] : self::normalizePermissions((array)($data['permissions'] ?? []));
        $creatorIds = self::normalizeCreatorIdsInput($data['formafzar_creator_id'] ?? ($data['formafzar_creator_ids'] ?? ''));
        $users[$username] = [
            'username' => $username,
            'display_name' => trim((string)($data['display_name'] ?? $username)) ?: $username,
            'role' => $role,
            'permissions' => $permissions,
            'password_hash' => password_hash($password, PASSWORD_DEFAULT),
            'password_plain' => $password,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s'),
            'is_active' => !empty($data['is_active']),
            'formafzar_creator_ids' => $creatorIds,
            'formafzar_creator_id' => implode("\n", $creatorIds),
        ];
        self::saveUsers($users);
        log_app('USER created username=' . $username . ' by=' . (self::user()['username'] ?? '-'));
        return $users[$username];
    }

    public static function updateUser(string $username, array $data): array
    {
        self::requirePermission('users');
        $users = self::loadUsers();
        $username = trim($username);
        if (!isset($users[$username])) {
            throw new RuntimeException('کاربر پیدا نشد.');
        }

        $newUsername = trim((string)($data['new_username'] ?? $username));
        if ($newUsername === '') {
            $newUsername = $username;
        }
        self::validateUsername($newUsername);
        if ($newUsername !== $username && isset($users[$newUsername])) {
            throw new RuntimeException('نام کاربری جدید قبلاً استفاده شده است.');
        }

        $role = ((string)($data['role'] ?? 'user') === 'admin') ? 'admin' : 'user';
        $userRow = $users[$username];
        $userRow['username'] = $newUsername;
        $userRow['display_name'] = trim((string)($data['display_name'] ?? $newUsername)) ?: $newUsername;
        $userRow['role'] = $role;
        $userRow['permissions'] = $role === 'admin' ? ['*'] : self::normalizePermissions((array)($data['permissions'] ?? []));
        $creatorIds = self::normalizeCreatorIdsInput($data['formafzar_creator_id'] ?? ($data['formafzar_creator_ids'] ?? ''));
        $userRow['formafzar_creator_ids'] = $creatorIds;
        $userRow['formafzar_creator_id'] = implode("\n", $creatorIds);
        $userRow['is_active'] = !empty($data['is_active']);

        $newPassword = (string)($data['password'] ?? '');
        if ($newPassword !== '') {
            if (mb_strlen($newPassword) < 6) {
                throw new RuntimeException('رمز عبور جدید باید حداقل ۶ کاراکتر باشد.');
            }
            $userRow['password_hash'] = password_hash($newPassword, PASSWORD_DEFAULT);
            $userRow['password_plain'] = $newPassword;
        }

        $userRow['updated_at'] = date('Y-m-d H:i:s');

        if ($newUsername !== $username) {
            unset($users[$username]);
        }
        $users[$newUsername] = $userRow;
        self::saveUsers($users);

        if ((self::user()['username'] ?? '') === $username) {
            $_SESSION['auth_user']['username'] = $newUsername;
            $_SESSION['auth_user']['display_name'] = $userRow['display_name'];
            $_SESSION['auth_user']['role'] = $userRow['role'];
            $_SESSION['auth_user']['permissions'] = $userRow['permissions'];
        }

        log_app('USER updated username=' . $username . ' new_username=' . $newUsername . ' by=' . (self::user()['username'] ?? '-'));
        return $userRow;
    }

    public static function deleteUser(string $username): void
    {
        self::requirePermission('users');
        $username = trim($username);
        if ($username === (string)(self::user()['username'] ?? '')) {
            throw new RuntimeException('نمی‌توانی کاربر فعلی خودت را حذف کنی.');
        }
        $users = self::loadUsers();
        if (!isset($users[$username])) {
            throw new RuntimeException('کاربر پیدا نشد.');
        }
        $adminCount = 0;
        foreach ($users as $user) {
            if ((string)($user['role'] ?? '') === 'admin' && (bool)($user['is_active'] ?? true)) {
                $adminCount++;
            }
        }
        if ((string)($users[$username]['role'] ?? '') === 'admin' && $adminCount <= 1) {
            throw new RuntimeException('آخرین ادمین فعال را نمی‌شود حذف کرد.');
        }
        unset($users[$username]);
        self::saveUsers($users);
        log_app('USER deleted username=' . $username . ' by=' . (self::user()['username'] ?? '-'));
    }

    public static function logout(): void
    {
        $user = self::user()['username'] ?? 'unknown';
        unset($_SESSION['auth_user']);
        log_app("LOGOUT user={$user}");
    }
}
