<?php

require_once BASE_PATH . '/config/database.php';

/**
 * Role permissions for the active PET SPA areas.
 * Admin is intentionally not configurable here: it always has full access.
 */
class Permission
{
    private PDO $db;
    private ?bool $tablesAvailable = null;
    private array $decisionCache = [];

    public function __construct()
    {
        $this->db = (new Database())->connect();
    }

    public static function definitions(): array
    {
        return [
            'customer.dashboard' => ['name' => 'Tổng quan khách hàng', 'module' => 'customer'],
            'customer.booking.create' => ['name' => 'Đặt lịch', 'module' => 'customer'],
            'customer.booking.view' => ['name' => 'Lịch hẹn của tôi', 'module' => 'customer'],
            'customer.notification.view' => ['name' => 'Thông báo', 'module' => 'customer'],
            'customer.profile.manage' => ['name' => 'Hồ sơ và mật khẩu', 'module' => 'customer'],
            'owner.dashboard' => ['name' => 'Tổng quan Owner', 'module' => 'owner'],
            'owner.booking.manage' => ['name' => 'Quản lý lịch hẹn', 'module' => 'owner'],
            'owner.service.manage' => ['name' => 'Quản lý dịch vụ', 'module' => 'owner'],
            'owner.service_category.manage' => ['name' => 'Danh mục dịch vụ', 'module' => 'owner'],
            'owner.business_hours.manage' => ['name' => 'Giờ phục vụ', 'module' => 'owner'],
            'owner.customer.view' => ['name' => 'Khách hàng', 'module' => 'owner'],
            'owner.post.manage' => ['name' => 'Quản lý bài viết', 'module' => 'owner'],
            'owner.post_category.manage' => ['name' => 'Danh mục bài viết', 'module' => 'owner'],
            'owner.gallery.manage' => ['name' => 'Thư viện', 'module' => 'owner'],
            'owner.contact.manage' => ['name' => 'Liên hệ', 'module' => 'owner'],
            'owner.notification.view' => ['name' => 'Thông báo Owner', 'module' => 'owner'],
            'owner.settings.manage' => ['name' => 'Cài đặt', 'module' => 'owner']
        ];
    }

    public static function matrixDefinition(): array
    {
        return [
            ['label' => 'Dashboard', 'customer' => 'customer.dashboard', 'owner' => 'owner.dashboard'],
            ['label' => 'Đặt lịch', 'customer' => 'customer.booking.create', 'owner' => null],
            ['label' => 'Lịch hẹn', 'customer' => 'customer.booking.view', 'owner' => 'owner.booking.manage'],
            ['label' => 'Dịch vụ', 'customer' => null, 'owner' => 'owner.service.manage'],
            ['label' => 'Danh mục dịch vụ', 'customer' => null, 'owner' => 'owner.service_category.manage'],
            ['label' => 'Giờ phục vụ', 'customer' => null, 'owner' => 'owner.business_hours.manage'],
            ['label' => 'Khách hàng', 'customer' => null, 'owner' => 'owner.customer.view'],
            ['label' => 'Bài viết', 'customer' => null, 'owner' => 'owner.post.manage'],
            ['label' => 'Danh mục bài viết', 'customer' => null, 'owner' => 'owner.post_category.manage'],
            ['label' => 'Thư viện', 'customer' => null, 'owner' => 'owner.gallery.manage'],
            ['label' => 'Liên hệ', 'customer' => null, 'owner' => 'owner.contact.manage'],
            ['label' => 'Cài đặt', 'customer' => null, 'owner' => 'owner.settings.manage'],
            ['label' => 'Thông báo', 'customer' => 'customer.notification.view', 'owner' => 'owner.notification.view'],
            ['label' => 'Hồ sơ / đổi mật khẩu', 'customer' => 'customer.profile.manage', 'owner' => null]
        ];
    }

    public function can(string $role, string $permissionKey): bool
    {
        if ($role === 'admin') {
            return true;
        }

        if (!in_array($role, ['customer', 'owner'], true)
            || !isset(self::definitions()[$permissionKey])) {
            return false;
        }

        $cacheKey = $role . ':' . $permissionKey;
        if (array_key_exists($cacheKey, $this->decisionCache)) {
            return $this->decisionCache[$cacheKey];
        }

        // Keep pre-migration installations usable, while new installations
        // fail closed for any key not declared by this class.
        if (!$this->hasPermissionTables()) {
            return $this->decisionCache[$cacheKey] = true;
        }

        $stmt = $this->db->prepare(
            'SELECT allowed FROM role_permissions
             WHERE role = :role AND permission_key = :permission_key
             LIMIT 1'
        );
        $stmt->execute([
            'role' => $role,
            'permission_key' => $permissionKey
        ]);
        $allowed = $stmt->fetchColumn();

        return $this->decisionCache[$cacheKey] = $allowed !== false
            && (int) $allowed === 1;
    }

    public function getMatrix(): array
    {
        $values = [];
        foreach (self::definitions() as $key => $_definition) {
            foreach (['customer', 'owner'] as $role) {
                $values[$role][$key] = $this->can($role, $key);
            }
        }

        return $values;
    }

    public function saveMatrix(array $submittedPermissions): void
    {
        if (!$this->hasPermissionTables()) {
            throw new RuntimeException(
                'Thiếu bảng phân quyền. Hãy chạy migration quyền trước khi lưu.'
            );
        }

        $definitions = self::definitions();
        $upsert = $this->db->prepare(
            'INSERT INTO role_permissions (role, permission_key, allowed)
             VALUES (:role, :permission_key, :allowed)
             ON DUPLICATE KEY UPDATE allowed = VALUES(allowed), updated_at = CURRENT_TIMESTAMP'
        );

        $ownsTransaction = !$this->db->inTransaction();
        if ($ownsTransaction) {
            $this->db->beginTransaction();
        }
        try {
            foreach (['customer', 'owner'] as $role) {
                $selected = $submittedPermissions[$role] ?? [];
                $selected = is_array($selected) ? $selected : [];

                foreach ($definitions as $permissionKey => $_definition) {
                    $belongsToRole = str_starts_with($permissionKey, $role . '.');
                    if (!$belongsToRole) {
                        continue;
                    }

                    $upsert->execute([
                        'role' => $role,
                        'permission_key' => $permissionKey,
                        'allowed' => in_array($permissionKey, $selected, true) ? 1 : 0
                    ]);
                }
            }
            if ($ownsTransaction) {
                $this->db->commit();
            }
        } catch (Throwable $error) {
            if ($ownsTransaction && $this->db->inTransaction()) {
                $this->db->rollBack();
            }
            throw $error;
        }

        $this->decisionCache = [];
    }

    private function hasPermissionTables(): bool
    {
        if ($this->tablesAvailable !== null) {
            return $this->tablesAvailable;
        }

        try {
            $result = $this->db->query(
                "SELECT COUNT(*) FROM information_schema.tables
                 WHERE table_schema = DATABASE()
                 AND table_name IN ('permissions', 'role_permissions')"
            );
            return $this->tablesAvailable = (int) $result->fetchColumn() === 2;
        } catch (Throwable) {
            return $this->tablesAvailable = false;
        }
    }
}
