<?php

declare(strict_types=1);

require_once __DIR__ . '/../ActivityLog.php';

/**
 * PermissionManager
 * 
 * Handles workspace role and permission checks.
 * Determines what a user can do within a specific workspace.
 */
class PermissionManager
{
    private static ?array $cachedPermissions = null;
    private static ?int $cachedUserId = null;
    private static ?int $cachedCompanyId = null;

    /**
     * All available permission keys in the system
     */
    public const PERMISSIONS = [
        // Dashboard
        'dashboard_view' => 'View Dashboard',
        
        // Clients
        'clients_view' => 'View Clients',
        'clients_create' => 'Create Clients',
        'clients_edit' => 'Edit Clients',
        'clients_delete' => 'Delete Clients',
        
        // Proposals
        'proposals_view' => 'View Proposals',
        'proposals_create' => 'Create Proposals',
        'proposals_edit' => 'Edit Proposals',
        'proposals_delete' => 'Delete Proposals',
        
        // Contracts
        'contracts_view' => 'View Contracts',
        'contracts_create' => 'Create Contracts',
        'contracts_edit' => 'Edit Contracts',
        'contracts_delete' => 'Delete Contracts',
        
        // Invoices
        'invoices_view' => 'View Invoices',
        'invoices_create' => 'Create Invoices',
        'invoices_edit' => 'Edit Invoices',
        'invoices_delete' => 'Delete Invoices',
        
        // Templates & Modules
        'templates_view' => 'View Templates',
        'templates_create' => 'Create Templates',
        'templates_edit' => 'Edit Templates',
        'templates_delete' => 'Delete Templates',
        'templates_manage' => 'Manage All Templates',
        
        // Team Members
        'members_view' => 'View Team Members',
        'activity_view' => 'View Activity Log',
        'members_manage' => 'Manage Team Members',
        'roles_manage' => 'Manage Roles',
        
        // Projects
        'projects_view' => 'View Projects',
        'projects_create' => 'Create Projects',
        'projects_edit' => 'Edit Projects',
        'projects_delete' => 'Delete Projects',
        'projects_manage' => 'Manage All Projects',
        'tasks_view' => 'View Tasks',
        'tasks_create' => 'Create Tasks',
        'tasks_edit' => 'Edit Tasks',
        'tasks_delete' => 'Delete Tasks',
        'tasks_assign' => 'Assign Tasks',
        
        // Owner Only
        'billing_view' => 'View Billing',
        'settings_manage' => 'Manage Workspace Settings',
    ];
    
    /**
     * Permission groups for UI display
     */
    public const PERMISSION_GROUPS = [
        'Dashboard' => ['dashboard_view'],
        'Clients' => ['clients_view', 'clients_create', 'clients_edit', 'clients_delete'],
        'Proposals' => ['proposals_view', 'proposals_create', 'proposals_edit', 'proposals_delete'],
        'Contracts' => ['contracts_view', 'contracts_create', 'contracts_edit', 'contracts_delete'],
        'Invoices' => ['invoices_view', 'invoices_create', 'invoices_edit', 'invoices_delete'],
        'Templates' => ['templates_view', 'templates_create', 'templates_edit', 'templates_delete', 'templates_manage'],
        'Projects' => ['projects_view', 'projects_create', 'projects_edit', 'projects_delete', 'projects_manage', 'tasks_view', 'tasks_create', 'tasks_edit', 'tasks_delete', 'tasks_assign'],
        'Team' => ['members_view', 'activity_view', 'members_manage', 'roles_manage'],
    ];

    /**
     * Owner-only permissions (cannot be granted to non-owners)
     */
    public const OWNER_ONLY = [
        'billing_view',
        'settings_manage',
    ];

    /**
     * Default permissions for the "Admin" role
     */
    public const DEFAULT_ADMIN_PERMISSIONS = [
        'dashboard_view', 'clients_view', 'clients_create', 'clients_edit', 'clients_delete',
        'proposals_view', 'proposals_create', 'proposals_edit', 'proposals_delete',
        'contracts_view', 'contracts_create', 'contracts_edit', 'contracts_delete',
        'invoices_view', 'invoices_create', 'invoices_edit', 'invoices_delete',
        'templates_view', 'templates_manage', 'members_view', 'members_manage', 'roles_manage',
        'projects_view', 'projects_create', 'projects_edit', 'projects_manage',
        'tasks_view', 'tasks_create', 'tasks_edit', 'tasks_assign',
    ];

    /**
     * Default permissions for the "Editor" role
     */
    public const DEFAULT_EDITOR_PERMISSIONS = [
        'dashboard_view', 'clients_view', 'clients_create', 'clients_edit',
        'proposals_view', 'proposals_create', 'proposals_edit',
        'contracts_view', 'contracts_create', 'contracts_edit',
        'invoices_view', 'invoices_create', 'invoices_edit',
        'templates_view',
    ];

    /**
     * Default permissions for the "Viewer" role
     */
    public const DEFAULT_VIEWER_PERMISSIONS = [
        'dashboard_view', 'clients_view', 'proposals_view', 'contracts_view', 'invoices_view',
    ];

    /**
     * Check if user is the owner of a workspace
     */
    public static function isOwner(int $userId, int $companyId): bool
    {
        $pdo = getPDO();
        
        // Check companies.owner_id first
        $stmt = $pdo->prepare("SELECT owner_id FROM companies WHERE id = ?");
        $stmt->execute([$companyId]);
        $company = $stmt->fetch();
        
        if ($company && (int)$company['owner_id'] === $userId) {
            return true;
        }
        
        // Fallback: check workspace_members.is_owner
        $stmt = $pdo->prepare("SELECT is_owner FROM workspace_members WHERE company_id = ? AND user_id = ?");
        $stmt->execute([$companyId, $userId]);
        $member = $stmt->fetch();
        
        return $member && (int)$member['is_owner'] === 1;
    }

    /**
     * Check if user has a specific permission in a workspace
     */
    public static function hasPermission(int $userId, int $companyId, string $permissionKey): bool
    {
        // Owners have all permissions
        if (self::isOwner($userId, $companyId)) {
            return true;
        }

        // Owner-only permissions cannot be granted to non-owners
        if (in_array($permissionKey, self::OWNER_ONLY, true)) {
            return false;
        }

        // Get user's permissions for this workspace
        $permissions = self::getUserPermissions($userId, $companyId);
        
        return in_array($permissionKey, $permissions, true);
    }

    /**
     * Get all permissions for a user in a workspace
     */
    public static function getUserPermissions(int $userId, int $companyId): array
    {
        // Return cached if available
        if (self::$cachedUserId === $userId && self::$cachedCompanyId === $companyId && self::$cachedPermissions !== null) {
            return self::$cachedPermissions;
        }

        $pdo = getPDO();

        // If owner, return all permissions
        if (self::isOwner($userId, $companyId)) {
            self::$cachedPermissions = array_keys(self::PERMISSIONS);
            self::$cachedUserId = $userId;
            self::$cachedCompanyId = $companyId;
            return self::$cachedPermissions;
        }

        // Get user's role in this workspace
        $stmt = $pdo->prepare("
            SELECT wm.role_id 
            FROM workspace_members wm 
            WHERE wm.company_id = ? AND wm.user_id = ? AND wm.status = 'active'
        ");
        $stmt->execute([$companyId, $userId]);
        $member = $stmt->fetch();

        if (!$member || !$member['role_id']) {
            // No role assigned, return empty permissions
            self::$cachedPermissions = [];
            self::$cachedUserId = $userId;
            self::$cachedCompanyId = $companyId;
            return [];
        }

        // Get permissions for this role
        $stmt = $pdo->prepare("
            SELECT permission_key 
            FROM role_permissions 
            WHERE role_id = ? AND allowed = 1
        ");
        $stmt->execute([$member['role_id']]);
        $permissions = $stmt->fetchAll(PDO::FETCH_COLUMN);

        // Filter out owner-only permissions
        $permissions = array_filter($permissions, fn($p) => !in_array($p, self::OWNER_ONLY, true));

        self::$cachedPermissions = $permissions;
        self::$cachedUserId = $userId;
        self::$cachedCompanyId = $companyId;

        return $permissions;
    }

    /**
     * Clear the permission cache (call after role/permission changes)
     */
    public static function clearCache(): void
    {
        self::$cachedPermissions = null;
        self::$cachedUserId = null;
        self::$cachedCompanyId = null;
    }

    /**
     * Get user's role in a workspace
     */
    public static function getUserRole(int $userId, int $companyId): ?array
    {
        $pdo = getPDO();

        $stmt = $pdo->prepare("
            SELECT wr.id, wr.name, wr.description, wm.is_owner
            FROM workspace_members wm
            LEFT JOIN workspace_roles wr ON wm.role_id = wr.id
            WHERE wm.company_id = ? AND wm.user_id = ? AND wm.status = 'active'
        ");
        $stmt->execute([$companyId, $userId]);
        $result = $stmt->fetch();

        if (!$result) {
            return null;
        }

        if ((int)$result['is_owner'] === 1) {
            return [
                'id' => null,
                'name' => 'Owner',
                'description' => 'Full access to all workspace features',
                'is_owner' => true,
            ];
        }

        return $result ? array_merge($result, ['is_owner' => false]) : null;
    }

    /**
     * Create default roles for a new workspace
     */
    public static function createDefaultRoles(int $companyId): void
    {
        $pdo = getPDO();

        $roles = [
            ['name' => 'Admin', 'description' => 'Full access except billing and settings', 'permissions' => self::DEFAULT_ADMIN_PERMISSIONS, 'is_default' => 0],
            ['name' => 'Editor', 'description' => 'Can create and edit content', 'permissions' => self::DEFAULT_EDITOR_PERMISSIONS, 'is_default' => 1],
            ['name' => 'Viewer', 'description' => 'Read-only access', 'permissions' => self::DEFAULT_VIEWER_PERMISSIONS, 'is_default' => 0],
        ];

        foreach ($roles as $role) {
            // Insert role
            $stmt = $pdo->prepare("
                INSERT INTO workspace_roles (company_id, name, description, is_default) 
                VALUES (?, ?, ?, ?)
                ON DUPLICATE KEY UPDATE description = VALUES(description)
            ");
            $stmt->execute([$companyId, $role['name'], $role['description'], $role['is_default']]);
            
            $roleId = $pdo->lastInsertId();
            if (!$roleId) {
                // Role already exists, get its ID
                $stmt = $pdo->prepare("SELECT id FROM workspace_roles WHERE company_id = ? AND name = ?");
                $stmt->execute([$companyId, $role['name']]);
                $existing = $stmt->fetch();
                $roleId = $existing['id'] ?? null;
            }

            if ($roleId) {
                // Insert permissions for this role
                foreach ($role['permissions'] as $permission) {
                    $stmt = $pdo->prepare("
                        INSERT INTO role_permissions (role_id, permission_key, allowed) 
                        VALUES (?, ?, 1)
                        ON DUPLICATE KEY UPDATE allowed = 1
                    ");
                    $stmt->execute([$roleId, $permission]);
                }
            }
        }
    }

    /**
     * Add owner as workspace member
     */
    public static function addOwnerAsMember(int $companyId, int $userId): void
    {
        $pdo = getPDO();

        // Update company owner_id
        $stmt = $pdo->prepare("UPDATE companies SET owner_id = ? WHERE id = ?");
        $stmt->execute([$userId, $companyId]);

        // Add to workspace_members as owner
        $stmt = $pdo->prepare("
            INSERT INTO workspace_members (company_id, user_id, is_owner, status, accepted_at)
            VALUES (?, ?, 1, 'active', NOW())
            ON DUPLICATE KEY UPDATE is_owner = 1, status = 'active'
        ");
        $stmt->execute([$companyId, $userId]);

        // Log owner joining the workspace (best-effort)
        try {
            ActivityLog::log(
                (int) $companyId,
                $userId,               // the owner themselves
                'member_joined',
                'user',
                $userId,
                null,
                null,
                null
            );
        } catch (Throwable $e) {
            // Ignore logging failures
        }
    }

    /**
     * Get all members of a workspace
     */
    public static function getWorkspaceMembers(int $companyId): array
    {
        $pdo = getPDO();

        $stmt = $pdo->prepare("
            SELECT 
                wm.id as member_id,
                wm.user_id,
                wm.role_id,
                wm.is_owner,
                wm.status,
                wm.invited_at,
                wm.accepted_at,
                u.name as user_name,
                u.email as user_email,
                wr.name as role_name,
                inv.name as invited_by_name
            FROM workspace_members wm
            JOIN users u ON wm.user_id = u.id
            LEFT JOIN workspace_roles wr ON wm.role_id = wr.id
            LEFT JOIN users inv ON wm.invited_by = inv.id
            WHERE wm.company_id = ?
            ORDER BY wm.is_owner DESC, wm.invited_at ASC
        ");
        $stmt->execute([$companyId]);

        return $stmt->fetchAll();
    }

    /**
     * Get all roles for a workspace
     */
    public static function getWorkspaceRoles(int $companyId): array
    {
        $pdo = getPDO();

        $stmt = $pdo->prepare("
            SELECT wr.*, 
                   (SELECT COUNT(*) FROM workspace_members wm WHERE wm.role_id = wr.id) as member_count
            FROM workspace_roles wr
            WHERE wr.company_id = ?
            ORDER BY wr.name ASC
        ");
        $stmt->execute([$companyId]);

        return $stmt->fetchAll();
    }

    /**
     * Get permissions for a specific role
     */
    public static function getRolePermissions(int $roleId): array
    {
        $pdo = getPDO();

        $stmt = $pdo->prepare("
            SELECT permission_key 
            FROM role_permissions 
            WHERE role_id = ? AND allowed = 1
        ");
        $stmt->execute([$roleId]);

        return $stmt->fetchAll(PDO::FETCH_COLUMN);
    }

    /**
     * Invite a user to a workspace
     */
    public static function inviteMember(int $companyId, int $userId, ?int $roleId, int $invitedBy): bool
    {
        $pdo = getPDO();

        try {
            $stmt = $pdo->prepare("
                INSERT INTO workspace_members (company_id, user_id, role_id, invited_by, status, invited_at)
                VALUES (?, ?, ?, ?, 'active', NOW())
                ON DUPLICATE KEY UPDATE role_id = VALUES(role_id), status = 'active', invited_by = VALUES(invited_by)
            ");
            $stmt->execute([$companyId, $userId, $roleId, $invitedBy]);

            // Also add to user_company_access for navigation compatibility
            $stmt = $pdo->prepare("
                INSERT INTO user_company_access (user_id, company_id, role, created_at)
                VALUES (?, ?, 'member', NOW())
                ON DUPLICATE KEY UPDATE role = 'member'
            ");
            $stmt->execute([$userId, $companyId]);

            // Log member joining the workspace (best-effort)
            try {
                ActivityLog::log(
                    (int) $companyId,
                    $userId,           // the member who now has access
                    'member_joined',
                    'user',
                    $userId,
                    null,
                    null,
                    [
                        'invited_by' => $invitedBy,
                        'role_id' => $roleId,
                    ]
                );
            } catch (Throwable $e) {
                // Ignore logging failures
            }

            self::clearCache();
            return true;
        } catch (Exception $e) {
            error_log("Failed to invite member: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Remove a member from a workspace
     */
    public static function removeMember(int $companyId, int $userId): bool
    {
        $pdo = getPDO();

        try {
            // Cannot remove owner
            if (self::isOwner($userId, $companyId)) {
                return false;
            }

            $stmt = $pdo->prepare("DELETE FROM workspace_members WHERE company_id = ? AND user_id = ?");
            $stmt->execute([$companyId, $userId]);

            $stmt = $pdo->prepare("DELETE FROM user_company_access WHERE company_id = ? AND user_id = ?");
            $stmt->execute([$companyId, $userId]);

            self::clearCache();
            return true;
        } catch (Exception $e) {
            error_log("Failed to remove member: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Update a member's role
     */
    public static function updateMemberRole(int $companyId, int $userId, ?int $roleId): bool
    {
        $pdo = getPDO();

        try {
            $stmt = $pdo->prepare("UPDATE workspace_members SET role_id = ? WHERE company_id = ? AND user_id = ?");
            $stmt->execute([$roleId, $companyId, $userId]);

            self::clearCache();
            return true;
        } catch (Exception $e) {
            error_log("Failed to update member role: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Find user by email or username
     */
    public static function findUserByEmailOrUsername(string $identifier): ?array
    {
        $pdo = getPDO();

        $stmt = $pdo->prepare("SELECT id, name, email FROM users WHERE email = ? OR name = ? LIMIT 1");
        $stmt->execute([$identifier, $identifier]);

        return $stmt->fetch() ?: null;
    }

    /**
     * Create a new user (for invite flow)
     */
    public static function createUser(string $name, string $email, string $password, int $companyId): ?int
    {
        $pdo = getPDO();

        try {
            $passwordHash = password_hash($password, PASSWORD_DEFAULT);
            
            $stmt = $pdo->prepare("
                INSERT INTO users (company_id, name, email, password_hash, role, is_active, created_at)
                VALUES (?, ?, ?, ?, 'member', 1, NOW())
            ");
            $stmt->execute([$companyId, $name, $email, $passwordHash]);

            return (int)$pdo->lastInsertId();
        } catch (Exception $e) {
            error_log("Failed to create user: " . $e->getMessage());
            return null;
        }
    }
}
