<?php

namespace PloverCRM\Core\Api\V1;

use PloverCRM\Common\CrmUserManager;
use PloverCRM\Common\LicenseChecker;
use PloverCRM\Common\Permissions;
use PloverCRM\Common\RoleManager;
use PloverCRM\Common\StandalonePasswordManager;
use PloverCRM\Core\Api\AbstractEndpoint;
use WP_REST_Request;

/**
 * User and seat management for CRM administrators.
 *
 * GET    /wp-json/plover-crm/v1/users                       - List CRM users
 * POST   /wp-json/plover-crm/v1/users                       - Create a native CRM user
 * PUT    /wp-json/plover-crm/v1/users/{id}/role             - Assign role to user
 * DELETE /wp-json/plover-crm/v1/users/{id}/role             - Remove role from user
 * GET    /wp-json/plover-crm/v1/users/{id}/password         - Show or generate the current password
 * POST   /wp-json/plover-crm/v1/users/{id}/password         - Regenerate the password
 */
class UsersEndpoint extends AbstractEndpoint
{
    public function getRoute(): string
    {
        return '/users(?:/(?P<id>[a-zA-Z0-9_-]+))?(?:/(?P<action>[a-z-]+))?';
    }

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

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

        if ($action === 'role') {
            if ($method === 'PUT') {
                return $this->assignRole($id, $request);
            }

            if ($method === 'DELETE') {
                return $this->removeRole($id);
            }
        }

        if ($action === 'password') {
            if ($method === 'GET') {
                return $this->showPassword($id);
            }

            if ($method === 'POST') {
                return $this->regeneratePassword($id);
            }
        }

        if ($method === 'GET' && !$id) {
            return $this->listUsersWithRoles();
        }

        if ($method === 'POST' && !$id && !$action) {
            return $this->createCrmUser($request);
        }

        return $this->error('The requested endpoint or method is not valid', 400, 'invalid_request');
    }

    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]);
        }

        $isReadRequest = $request->get_method() === 'GET' && !$request->get_param('action');
        $action = $isReadRequest ? 'view' : 'manage';

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

        return true;
    }

    /**
     * List users that currently consume CRM seats.
     */
    private function listUsersWithRoles()
    {
        $users = array_map(function ($user) {
            $user['has_standalone_password'] = StandalonePasswordManager::hasPassword((int) $user['id']);
            return $user;
        }, RoleManager::getUsersWithRoles());

        return $this->success([
            'users' => $users,
        ], [
            'total' => count($users),
            'native_total' => count($users),
            'seat_usage' => LicenseChecker::getCrmUserSeatUsage(),
        ]);
    }

    /**
     * Create a new native CRM user.
     */
    private function createCrmUser(WP_REST_Request $request)
    {
        $result = CrmUserManager::createNativeUser((array) $request->get_json_params());

        if (is_wp_error($result)) {
            $status = $result->get_error_code() === 'crm_user_limit_reached' ? 403 : 400;
            $errorData = $result->get_error_data();

            if (!is_array($errorData)) {
                $errorData = [];
            }

            $errorData['code'] = $result->get_error_code();

            return $this->error($result->get_error_message(), $status, $errorData);
        }

        return $this->success(
            $result,
            ['created' => !empty($result['created'])],
            !empty($result['created']) ? 201 : 200
        );
    }

    /**
     * Assign a CRM role to a native CRM user.
     */
    private function assignRole($userId, WP_REST_Request $request)
    {
        if (empty($userId) || !is_numeric($userId)) {
            return $this->error('A valid user ID is required', 400, 'invalid_user_id');
        }

        $userId = (int) $userId;
        $user = CrmUserManager::getByPublicId($userId);

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

        $data = (array) $request->get_json_params();
        $roleId = sanitize_text_field($data['role_id'] ?? '');

        if ($roleId === '') {
            return $this->error('Role ID is required', 400, 'missing_role_id');
        }

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

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

        if (RoleManager::isWordPressAdministrator((int) $user['wp_user_id'])) {
            return $this->error(
                'WordPress administrators already have full CRM access and cannot be assigned a CRM role.',
                400,
                'wordpress_admin_role_not_allowed'
            );
        }

        if (empty($user['role_id']) && !LicenseChecker::canAssignAnotherCrmUser()) {
            $usage = LicenseChecker::getCrmUserSeatUsage();

            return $this->error(
                'CRM user limit reached for this site. Remove a CRM user or upgrade your license.',
                403,
                [
                    'code' => 'crm_user_limit_reached',
                    'seat_limit' => $usage['limit'],
                    'seat_used' => $usage['used'],
                ]
            );
        }

        if (!RoleManager::assignRoleToUser($userId, $roleId)) {
            return $this->error('Unable to assign role to user', 500, 'role_assignment_failed');
        }

        $passwordBundle = StandalonePasswordManager::getPasswordBundle($userId, [
            'rotated_by_user_id' => get_current_user_id() ?: null,
        ]);

        if (!$passwordBundle) {
            RoleManager::removeRoleFromUser($userId);
            return $this->error('Unable to create a password for this user', 500, 'password_generation_failed');
        }

        return $this->success([
            'user_id' => $userId,
            'role_id' => $roleId,
            'role_name' => $role['name'],
        ], ['assigned' => true]);
    }

    /**
     * Remove a CRM role and revoke standalone access if necessary.
     */
    private function removeRole($userId)
    {
        if (empty($userId) || !is_numeric($userId)) {
            return $this->error('A valid user ID is required', 400, 'invalid_user_id');
        }

        $userId = (int) $userId;
        $user = CrmUserManager::getByPublicId($userId);

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

        $currentUser = wp_get_current_user();
        $assignedRole = RoleManager::getUserRole((int) $user['wp_user_id']);

        if (
            $currentUser &&
            (int) $currentUser->ID === (int) $user['wp_user_id'] &&
            $assignedRole &&
            !empty($assignedRole['is_super_admin']) &&
            !RoleManager::isWordPressAdministrator((int) $user['wp_user_id'])
        ) {
            return $this->error('You cannot remove your own super admin role', 403, 'self_role_removal_denied');
        }

        if (RoleManager::isWordPressAdministrator((int) $user['wp_user_id'])) {
            if (!RoleManager::removeRoleFromUser($userId)) {
                return $this->error('Unable to clear the stored role for this site owner', 500, 'role_removal_failed');
            }
        } else {
            if (!CrmUserManager::deleteNativeUser($userId)) {
                return $this->error('Unable to remove this CRM user', 500, 'role_removal_failed');
            }
        }

        if (!RoleManager::userHasAccess((int) $user['wp_user_id'])) {
            StandalonePasswordManager::revokePasswordForUser($userId);
        }

        return $this->success([
            'user_id' => $userId,
        ], ['removed' => true]);
    }

    /**
     * Show the current standalone password for a CRM-enabled user.
     */
    private function showPassword($userId)
    {
        if (empty($userId) || !is_numeric($userId)) {
            return $this->error('A valid user ID is required', 400, 'invalid_user_id');
        }

        $userId = (int) $userId;
        $user = CrmUserManager::getByPublicId($userId);

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

        if (!RoleManager::userHasAccess((int) $user['wp_user_id'])) {
            return $this->error('User must have CRM access to receive sign-in details', 400, 'crm_access_required');
        }

        $bundle = StandalonePasswordManager::getPasswordBundle($userId, [
            'rotated_by_user_id' => get_current_user_id() ?: null,
        ]);

        if (!$bundle) {
            return $this->error('Unable to load the sign-in details for this user', 500, 'password_unavailable');
        }

        return $this->success([
            'user_id' => $userId,
            'password' => $bundle['password'],
            'record' => $bundle['record'],
            'share' => $bundle['share'],
        ]);
    }

    /**
     * Regenerate the standalone password for a CRM-enabled user.
     */
    private function regeneratePassword($userId)
    {
        if (empty($userId) || !is_numeric($userId)) {
            return $this->error('A valid user ID is required', 400, 'invalid_user_id');
        }

        $userId = (int) $userId;
        $user = CrmUserManager::getByPublicId($userId);

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

        if (!RoleManager::userHasAccess((int) $user['wp_user_id'])) {
            return $this->error('User must have CRM access to receive sign-in details', 400, 'crm_access_required');
        }

        $bundle = StandalonePasswordManager::regeneratePassword($userId, [
            'rotated_by_user_id' => get_current_user_id() ?: null,
        ]);

        if (!$bundle) {
            return $this->error('Unable to reset the password for this user', 500, 'password_rotation_failed');
        }

        return $this->success([
            'user_id' => $userId,
            'password' => $bundle['password'],
            'record' => $bundle['record'],
            'share' => $bundle['share'],
            'message' => 'Password reset successfully.',
        ], ['updated' => true]);
    }
}
