<?php

namespace PloverCRM\Core\Api\V1;

use PloverCRM\Core\Api\AbstractEndpoint;
use PloverCRM\Common\CapabilityRegistry;
use PloverCRM\Common\Permissions;
use PloverCRM\Common\RoleManager;
use WP_REST_Request;

/**
 * Roles Endpoint
 * 
 * GET    /wp-json/plover-crm/v1/roles           - List all roles
 * GET    /wp-json/plover-crm/v1/roles/fields    - Get available fields for permissions
 * GET    /wp-json/plover-crm/v1/roles/{id}      - Get single role
 * POST   /wp-json/plover-crm/v1/roles           - Create role
 * PUT    /wp-json/plover-crm/v1/roles/{id}      - Update role
 * DELETE /wp-json/plover-crm/v1/roles/{id}      - Delete role
 */
class RolesEndpoint extends AbstractEndpoint
{
    public function getRoute(): string
    {
        return '/roles(?:/(?P<id>[a-zA-Z0-9_-]+))?';
    }

    public function getMethod()
    {
        return ['GET', 'POST', 'PUT', 'DELETE'];
    }

    public function handle(WP_REST_Request $request)
    {
        $method = $request->get_method();
        $id = $request->get_param('id');

        // Handle /roles/fields specially
        if ($id === 'fields' && $method === 'GET') {
            return $this->getAvailableFields();
        }

        switch ($method) {
            case 'GET':
                return $id ? $this->getRole($id) : $this->listRoles();

            case 'POST':
                return $this->createRole($request);

            case 'PUT':
                return $this->updateRole($id, $request);

            case 'DELETE':
                return $this->deleteRole($id);

            default:
                return $this->error('The requested method is not supported for this endpoint', 405);
        }
    }

    public function checkPermission(WP_REST_Request $request)
    {
        $passwordAuth = $this->authenticateWithPassword($request);

        if ($passwordAuth) {
            wp_set_current_user((int) $passwordAuth['user_id']);
        }

        $user = wp_get_current_user();

        if (!$user || !$user->ID) {
            return new \WP_Error('unauthorized', 'Authentication is required to access this resource', ['status' => 401]);
        }

        $method = $request->get_method();
        $id = (string) ($request->get_param('id') ?? '');

        if ($method === 'GET') {
            $canViewRoles = Permissions::canAccessSetting('roles', 'view');
            $canManageUsers = Permissions::canAccessSetting('users', 'manage');

            if ($id === '') {
                if ($canViewRoles || $canManageUsers) {
                    return true;
                }
            } elseif ($canViewRoles) {
                return true;
            }

            return new \WP_Error('forbidden', 'You do not have permission to view CRM roles.', ['status' => 403]);
        }

        if (!Permissions::canAccessSetting('roles', 'manage')) {
            return new \WP_Error('forbidden', 'You do not have permission to manage CRM roles.', ['status' => 403]);
        }

        return true;
    }

    /**
     * List all roles
     */
    private function listRoles()
    {
        $usageCounts = RoleManager::getRoleUsageCounts();
        $roles = array_map(
            static fn(array $role): array => RoleManager::appendUsageToRole($role, $usageCounts),
            RoleManager::getAllRoles()
        );

        return $this->success([
            'roles' => $roles
        ], [
            'total' => count($roles)
        ]);
    }

    /**
     * Get single role
     */
    private function getRole(string $id)
    {
        $role = RoleManager::getRole($id);

        if (!$role) {
            return $this->error('The requested role does not exist', 404, 'role_not_found');
        }

        return $this->success(RoleManager::appendUsageToRole($role));
    }

    /**
     * Get available fields for permission matrix
     */
    private function getAvailableFields()
    {
        $fields = RoleManager::getAvailableFields();

        return $this->success([
            'standard' => $fields['standard'] ?? [],
            'custom' => $fields['custom'] ?? [],
            'tracking' => $fields['tracking'] ?? [],
            'entities' => RoleManager::getAvailableEntities(),
            'settings' => CapabilityRegistry::getSettingDefinitions(),
            'scopes' => RoleManager::getAvailableScopes(),
        ]);
    }

    /**
     * Create new role
     */
    private function createRole(WP_REST_Request $request)
    {
        $data = $request->get_json_params();

        if (empty($data['name'])) {
            return $this->error('Role name is required', 400, 'missing_role_name');
        }

        $role = RoleManager::createRole($data);

        if (!$role) {
            return $this->error('Unable to create role. A role with this identifier may already exist.', 409, 'role_creation_failed');
        }

        return $this->success(RoleManager::appendUsageToRole($role), ['created' => true], 201);
    }

    /**
     * Update role
     */
    private function updateRole(?string $id, WP_REST_Request $request)
    {
        if (empty($id)) {
            return $this->error('Role ID is required', 400, 'missing_role_id');
        }

        $data = $request->get_json_params();

        if (empty($data)) {
            return $this->error('Request body must contain valid role data', 400, 'invalid_request_body');
        }

        $role = RoleManager::updateRole($id, $data);

        if (!$role) {
            return $this->error('The requested role does not exist', 404, 'role_not_found');
        }

        return $this->success(RoleManager::appendUsageToRole($role), ['updated' => true]);
    }

    /**
     * Delete role
     */
    private function deleteRole(?string $id)
    {
        if (empty($id)) {
            return $this->error('Role ID is required', 400, 'missing_role_id');
        }

        $role = RoleManager::getRole($id);

        if (!$role) {
            return $this->error('The requested role does not exist', 404, 'role_not_found');
        }

        if (!empty($role['is_protected']) || !empty($role['is_super_admin'])) {
            return $this->error('This protected role cannot be deleted', 403, 'protected_role');
        }

        $assignedUsersCount = RoleManager::countUsersWithRole($id);

        if ($assignedUsersCount > 0) {
            return $this->error(
                'This role is still assigned to CRM users. Reassign those users before deleting the role.',
                409,
                [
                    'code' => 'role_in_use',
                    'assigned_users_count' => $assignedUsersCount,
                ]
            );
        }

        $result = RoleManager::deleteRole($id);

        if (!$result) {
            return $this->error('Unable to delete role', 500, 'role_deletion_failed');
        }

        return $this->success(['deleted' => true]);
    }
}
