<?php
/**
 * Authentication Controller
 *
 * Handles authentication-related endpoints including nonce refresh,
 * session validation, and authentication status.
 *
 * @package FleekDash\Controllers
 */

namespace FleekDash\Controllers;

use FleekDash\Core\AuthMiddleware;
use FleekDash\Services\ResponseService;
use FleekDash\Utils\AvatarHelper;

class AuthController extends BaseController
{
    private const API_NAMESPACE = 'fleekdash/v1';
    private const MSG_INTERNAL_ERROR = 'Internal server error';

    /**
     * Register REST routes for authentication
     */
    public function register_routes() // NOSONAR php:S100 - WP REST route registration convention
    {
        $ns = self::API_NAMESPACE;
        // Public endpoints (no authentication required)
        \register_rest_route($ns, '/login', array(
            'methods' => 'POST',
            'callback' => array($this, 'login'),
            'permission_callback' => [$this, 'allowPublicAuthEndpoint']
        ));

        \register_rest_route(self::API_NAMESPACE, '/register', array(
            'methods' => 'POST',
            'callback' => array($this, 'register'),
            'permission_callback' => [$this, 'allowPublicAuthEndpoint']
        ));

        \register_rest_route(self::API_NAMESPACE, '/forgot-password', array(
            'methods' => 'POST',
            'callback' => array($this, 'forgot_password'),
            'permission_callback' => [$this, 'allowPublicAuthEndpoint']
        ));

        // Protected endpoints (authentication required)
        \register_rest_route(self::API_NAMESPACE, '/auth/refresh-nonce', array(
            'methods' => 'POST',
            'callback' => array($this, 'refresh_nonce'),
            'permission_callback' => [AuthMiddleware::class, 'createAuthenticatedPermissionCallback'],
        ));

        \register_rest_route(self::API_NAMESPACE, '/auth/validate', array(
            'methods' => 'GET',
            'callback' => array($this, 'validate'),
            'permission_callback' => [AuthMiddleware::class, 'createAuthenticatedPermissionCallback'],
        ));

        \register_rest_route(self::API_NAMESPACE, '/auth/status', array(
            'methods' => 'GET',
            'callback' => array($this, 'status'),
            'permission_callback' => [$this, 'allowPublicAuthEndpoint']
        ));

        \register_rest_route(self::API_NAMESPACE, '/auth/logout', array(
            'methods' => 'POST',
            'callback' => array($this, 'logout'),
            'permission_callback' => [AuthMiddleware::class, 'createAuthenticatedPermissionCallback'],
        ));

        \register_rest_route(self::API_NAMESPACE, '/auth/check-capability', array(
            'methods' => 'GET',
            'callback' => array($this, 'check_capability'),
            'permission_callback' => [AuthMiddleware::class, 'createAuthenticatedPermissionCallback'],
        ));
    }

    public function allowPublicAuthEndpoint(): bool
    {
        return true;
    }

    /**
     * Refresh nonce - public endpoint that checks authentication internally
     *
     * @param \WP_REST_Request $_request Unused; required by REST callback signature.
     * @return \WP_REST_Response
     */
    public function refresh_nonce(\WP_REST_Request $_request) // NOSONAR php:S100,S1172 - WP REST callback signature
    {
        try {
            // Check if user is authenticated using WordPress session
            if (!\is_user_logged_in()) {
                return ResponseService::error(
                    'Authentication required to refresh nonce',
                    ['authenticated' => false],
                    401,
                );
            }

            // Generate new nonce
            $nonce = \wp_create_nonce('wp_rest');

            // Update cache
            $user_id = \get_current_user_id();
            $cache_key = "auth_nonce_{$user_id}";
            wp_cache_set($cache_key, $nonce, 'fleekdash_auth', 300); // 5 minutes

            return ResponseService::success([
                'nonce' => $nonce,
                'expires_in' => 300,
                'user_id' => $user_id,
                'authenticated' => true
            ]);

        } catch (\Exception $e) {
            return ResponseService::error(
                'Failed to refresh nonce',
                ['error' => $e->getMessage()],
                500,
            );
        }
    }

    /**
     * Validate authentication status
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function validate(\WP_REST_Request $request)
    {
        try {
            $level = $request->get_param('level') ?? 'authenticated';
            $auth_check = AuthMiddleware::check($level);

            return ResponseService::success([
                'authenticated' => $auth_check['authenticated'],
                'level' => $level,
                'user_id' => \get_current_user_id(),
                'reason' => $auth_check['reason'] ?? null
            ]);

        } catch (\Exception $e) {
            return ResponseService::error(
                'Validation failed',
                ['error' => $e->getMessage()],
                500,
            );
        }
    }

    /**
     * Try to authenticate via cookie when REST_REQUEST and user_id is 0.
     *
     * @param int $user_id Current user ID (by reference)
     * @return array Debug cookies info
     */
    private function tryAuthenticateViaCookie(&$user_id): array
    {
        $debug_cookies = [];
        if (!defined('REST_REQUEST') || !REST_REQUEST || $user_id !== 0) {
            return $debug_cookies;
        }

        $cookie_hash = COOKIEHASH;
        $cookie_name = "wordpress_logged_in_{$cookie_hash}";
        if (!isset($_COOKIE[$cookie_name])) {
            $debug_cookies['cookie_missing'] = $cookie_name;
            return $debug_cookies;
        }

        $debug_cookies['cookie_found'] = true;
        $authenticated_user_id = wp_validate_auth_cookie('', 'logged_in');
        if ($authenticated_user_id) {
            $user_id = $authenticated_user_id;
            \wp_set_current_user($user_id);
            $debug_cookies['auth_success'] = true;
        } else {
            $debug_cookies['auth_failed'] = true;
        }
        return $debug_cookies;
    }

    /**
     * Build user object for status response.
     *
     * @param int $user_id User ID
     * @param \WP_User $user User object
     * @return array User data for status
     */
    private function buildStatusUserData(int $user_id, \WP_User $user): array
    {
        $avatar_url = AvatarHelper::getUserAvatarUrl($user_id, ['size' => 96]);
        return [
            'id' => $user_id,
            'username' => $user->user_login,
            'email' => $user->user_email,
            'name' => $user->display_name,
            'display_name' => $user->display_name,
            'first_name' => $user->first_name,
            'last_name' => $user->last_name,
            'nickname' => $user->nickname,
            'role' => $user->roles[0] ?? '',
            'roles' => $user->roles,
            'avatar_url' => $avatar_url,
            'avatar' => $avatar_url,
            'registered' => $user->user_registered,
            'url' => $user->user_url,
        ];
    }

    /**
     * Build debug info for status response when WP_DEBUG is enabled.
     *
     * @param array $debug_cookies Cookie debug data
     * @param bool $is_logged_in Whether user is logged in
     * @param \WP_User|null $user Current user or null
     * @return array Debug info array
     */
    private function buildDebugInfo(array $debug_cookies, bool $is_logged_in, $user): array
    {
        return [
            'cookie_debug' => $debug_cookies,
            'cookies_sent' => !empty($_COOKIE),
            'session_id' => session_id() ?: null,
            'auth_cookies' => [
                'logged_in' => !empty($_COOKIE[LOGGED_IN_COOKIE]),
                'auth' => !empty($_COOKIE[AUTH_COOKIE]),
                'secure_auth' => !empty($_COOKIE[SECURE_AUTH_COOKIE]),
            ],
            'user_roles' => ($is_logged_in && $user) ? $user->roles : [],
            'user_login' => ($is_logged_in && $user) ? $user->user_login : null,
        ];
    }

    /**
     * Get authentication status with enhanced SPA support
     *
     * @param \WP_REST_Request $_request Unused; required by REST callback signature.
     * @return \WP_REST_Response
     */
    public function status(\WP_REST_Request $_request) // NOSONAR php:S1172 - WP REST callback signature
    {
        try {
            $user_id = \get_current_user_id();
            $debug_cookies = $this->tryAuthenticateViaCookie($user_id);

            $is_logged_in = \is_user_logged_in() && $user_id > 0;
            $user = $is_logged_in ? \wp_get_current_user() : null;

            $status_data = [
                'authenticated' => $is_logged_in,
                'user_id' => $user_id,
                'capabilities' => ($is_logged_in && $user) ? array_keys($user->allcaps) : [],
                'nonce' => $is_logged_in ? \wp_create_nonce('wp_rest') : null,
                'timestamp' => time(),
            ];

            if ($is_logged_in && $user) {
                $status_data['user'] = $this->buildStatusUserData($user_id, $user);
            }

            if (defined('WP_DEBUG') && WP_DEBUG) {
                $status_data['debug_info'] = $this->buildDebugInfo($debug_cookies, $is_logged_in, $user);
            }

            return ResponseService::success($status_data);
        } catch (\Exception $e) {
            return ResponseService::error(
                'Status check failed',
                ['error' => $e->getMessage(), 'timestamp' => time()],
                500,
            );
        }
    }

    /**
     * Logout user
     *
     * @param \WP_REST_Request $_request
     * @return \WP_REST_Response
     */
    public function logout(\WP_REST_Request $_request) // NOSONAR php:S1172 - WP REST callback signature
    {
        try {
            $user_id = \get_current_user_id();

            if ($user_id) {
                // Clear auth cache
                $cache_key = "auth_nonce_{$user_id}";
                wp_cache_delete($cache_key, 'fleekdash_auth');

                // WordPress logout
                wp_logout();
            }

            return ResponseService::success([
                'message' => 'Logged out successfully',
                'authenticated' => false
            ]);

        } catch (\Exception $e) {
            return ResponseService::error(
                'Logout failed',
                ['error' => $e->getMessage()],
                500,
            );
        }
    }

    /**
     * Check if user has specific capability
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function check_capability(\WP_REST_Request $request) // NOSONAR php:S100 - WP REST callback convention
    {
        try {
            $capability = $request->get_param('capability');

            if (!$capability) {
                return ResponseService::error('Capability parameter is required', null, 400);
            }

            $user_id = \get_current_user_id();
            $current_user = \wp_get_current_user();
            $user_role = ($current_user && !empty($current_user->roles)) ? $current_user->roles[0] : 'none';
            $has_capability = \current_user_can($capability);

            return ResponseService::success('Capability check completed', [
                'user_id' => $user_id,
                'user_role' => $user_role,
                'capability' => $capability,
                'has_capability' => $has_capability,
                'timestamp' => time()
            ]);

        } catch (\Exception $e) {
            return ResponseService::error(self::MSG_INTERNAL_ERROR, null, 500);
        }
    }

    /**
     * Check multiple capabilities at once
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function check_multiple_capabilities(\WP_REST_Request $request) // NOSONAR php:S100 - WP REST callback convention
    {
        try {
            $capabilities = $request->get_param('capabilities');

            if (!is_array($capabilities) || empty($capabilities)) {
                return ResponseService::error('Capabilities must be a non-empty array', null, 400);
            }

            $user_id = \get_current_user_id();
            $current_user = \wp_get_current_user();
            $user_role = ($current_user && !empty($current_user->roles)) ? $current_user->roles[0] : 'none';

            $results = [];
            foreach ($capabilities as $capability) {
                if (is_string($capability) && !empty($capability)) {
                    $results[$capability] = \current_user_can($capability);
                }
            }

            return ResponseService::success('Multiple capability check completed', [
                'user_id' => $user_id,
                'user_role' => $user_role,
                'results' => $results,
                'capabilities_checked' => count($results),
                'timestamp' => time()
            ]);

        } catch (\Exception $e) {
            return ResponseService::error(self::MSG_INTERNAL_ERROR, null, 500);
        }
    }

    /**
     * Get current user capabilities summary
     *
     * @param \WP_REST_Request $_request Unused; required by REST callback signature.
     * @return \WP_REST_Response
     */
    public function get_user_capabilities(\WP_REST_Request $_request) // NOSONAR php:S100,S1172 - WP REST callback signature
    {
        try {
            $user_id = \get_current_user_id();
            $current_user = \wp_get_current_user();

            if (!$user_id || !\is_user_logged_in()) {
                return ResponseService::success('User not logged in', [
                    'user_id' => 0,
                    'user_role' => 'none',
                    'capabilities' => [],
                    'total_capabilities' => 0
                ]);
            }

            $user_role = ($current_user && !empty($current_user->roles)) ? $current_user->roles[0] : 'none';
            $all_capabilities = $current_user->allcaps ?? [];
            $user_capabilities = array_keys(array_filter($all_capabilities));

            return ResponseService::success('User capabilities retrieved', [
                'user_id' => $user_id,
                'user_role' => $user_role,
                'user_login' => $current_user->user_login ?? '',
                'user_email' => $current_user->user_email ?? '',
                'capabilities' => $user_capabilities,
                'total_capabilities' => count($user_capabilities),
                'timestamp' => time()
            ]);

        } catch (\Exception $e) {
            return ResponseService::error(self::MSG_INTERNAL_ERROR, null, 500);
        }
    }

    /**
     * Validate registration params. Returns error response or null if valid.
     *
     * @param array $params Sanitized params
     * @return \WP_REST_Response|null Error response or null
     */
    private function validateRegistrationParams(array $params): ?\WP_REST_Response
    {
        $email = \sanitize_email($params['email'] ?? '');
        $username = \sanitize_text_field($params['username'] ?? '');
        $password = $params['password'] ?? '';

        $checks = [
            [empty($email) || !\is_email($email), __('Enter a valid email address.', 'fleekdash')],
            [empty($username) || strlen($username) < 3, __('Username must have at least 3 characters.', 'fleekdash')],
            [empty($password) || strlen($password) < 6, __('Password must have at least 6 characters.', 'fleekdash')],
            [email_exists($email), __('Email already in use. Try signing in.', 'fleekdash')],
            [username_exists($username), __('Username already in use. Pick another.', 'fleekdash')],
        ];
        foreach ($checks as [$fail, $msg]) {
            if ($fail) {
                return ResponseService::error($msg, null, 400);
            }
        }
        return null;
    }

    /**
     * Execute user registration (create user, set role, auto-login).
     *
     * @param array $params Registration params
     * @return \WP_REST_Response
     */
    private function executeRegistration(array $params): \WP_REST_Response
    {
        $email = \sanitize_email($params['email'] ?? '');
        $username = \sanitize_text_field($params['username'] ?? '');
        $password = $params['password'] ?? '';
        $user_id = \wp_create_user($username, $password, $email);

        if (is_wp_error($user_id)) {
            return ResponseService::error(
                sprintf(__('Could not create account: %s', 'fleekdash'), $user_id->get_error_message()),
                null,
                400,
            );
        }

        // wp_update_user() does not support the 'role' parameter - use WP_User::set_role() instead (see UserController::executeStore)
        $user = new \WP_User($user_id);
        $user->set_role('subscriber');

        \wp_set_current_user($user_id);
        wp_set_auth_cookie($user_id, true);
        $nonce = \wp_create_nonce('wp_rest');

        return ResponseService::success('User registered successfully', [
            'user_id' => $user_id,
            'username' => $username,
            'email' => $email,
            'authenticated' => true,
            'nonce' => $nonce,
        ]);
    }

    /**
     * User registration endpoint
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function register(\WP_REST_Request $request)
    {
        try {
            $params = $request->get_json_params();
            $validation_error = $this->validateRegistrationParams($params);
            if ($validation_error !== null) {
                return $validation_error;
            }
            return $this->executeRegistration($params);
        } catch (\Exception $e) {
            return ResponseService::error(
                __('Registration failed. Please try again shortly.', 'fleekdash'),
                ['error' => $e->getMessage()],
                500,
            );
        }
    }

    /**
     * Execute forgot-password flow (generate key, send email).
     *
     * @param string $email Sanitized email
     * @return \WP_REST_Response
     */
    private function executeForgotPassword(string $email): \WP_REST_Response
    {
        $success_msg = 'If the email address exists, a password reset link has been sent';
        $user = \get_user_by('email', $email);
        if (!$user) {
            return ResponseService::success($success_msg);
        }
        $key = get_password_reset_key($user);
        if (is_wp_error($key)) {
            return ResponseService::error('Failed to generate reset key', null, 500);
        }
        $reset_link = add_query_arg([
            'action' => 'rp',
            'key' => $key,
            'login' => rawurlencode($user->user_login)
        ], wp_login_url());
        $subject = sprintf('[%s] Password Reset Request', \get_bloginfo('name'));
        $message = sprintf(
            "Hello %s,\n\nYou have requested to reset your password for your account on %s.\n\nTo reset your password, visit:\n%s\n\nIf you did not request this, ignore this email.\n\nBest regards,\n%s",
            $user->display_name,
            \get_bloginfo('name'),
            $reset_link,
            \get_bloginfo('name'),
        );
        $sent = \wp_mail($email, $subject, $message, ['Content-Type: text/html; charset=UTF-8']);
        return $sent
            ? ResponseService::success($success_msg)
            : ResponseService::error('Failed to send reset email', null, 500);
    }

    /**
     * Password reset request endpoint
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function forgot_password(\WP_REST_Request $request) // NOSONAR php:S100 - WP REST callback convention
    {
        try {
            $params = $request->get_json_params();
            $email = \sanitize_email($params['email'] ?? '');
            if (empty($email) || !\is_email($email)) {
                return ResponseService::error('Valid email address is required', null, 400);
            }
            return $this->executeForgotPassword($email);
        } catch (\Exception $e) {
            return ResponseService::error(
                'Password reset request failed',
                ['error' => $e->getMessage()],
                500,
            );
        }
    }

    /**
     * Authenticate user (demo bypass or wp_authenticate) and perform login.
     *
     * @param string $username Username or email
     * @param string $password Password
     * @param bool $remember Remember login
     * @return \WP_REST_Response
     */
    private function processLogin(string $username, string $password, bool $remember): \WP_REST_Response
    {
        $user = $this->resolveAuthenticatedUser($username, $password);
        if (is_wp_error($user)) {
            return ResponseService::error('Invalid username or password', null, 401);
        }
        if (!is_object($user) || !property_exists($user, 'user_login') || !property_exists($user, 'user_email')) {
            return ResponseService::error('Authentication failed', null, 401);
        }
        \wp_set_current_user($user->ID);
        wp_set_auth_cookie($user->ID, $remember);
        $nonce = \wp_create_nonce('wp_rest');
        $user_data = get_object_vars($user);
        return ResponseService::success('Login successful', [
            'user_id' => $user->ID,
            'username' => $user_data['user_login'] ?? '',
            'email' => $user_data['user_email'] ?? '',
            'authenticated' => true,
            'nonce' => $nonce,
        ]);
    }

    /**
     * Resolve user via demo token or wp_authenticate.
     *
     * @param string $username Username or email
     * @param string $password Password
     * @return \WP_User|\WP_Error|null
     */
    private function resolveAuthenticatedUser(string $username, string $password)
    {
        $demo_enabled = (bool)\get_option('fleekdash_login_demo_enabled', false);
        $demo_user_id = (int)\get_option('fleekdash_login_demo_user_id', 0);
        $demo_token = \get_option('fleekdash_login_demo_token', '');
        if ($demo_enabled && $demo_user_id > 0 && !empty($demo_token) && $password === $demo_token) {
            $demo_user = \get_userdata($demo_user_id);
            if ($demo_user && ($username === $demo_user->user_login || $username === $demo_user->user_email)) {
                return $demo_user;
            }
        }
        return wp_authenticate($username, $password);
    }

    /**
     * Login endpoint
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function login(\WP_REST_Request $request)
    {
        try {
            $params = $request->get_json_params();
            $username = \sanitize_text_field($params['username'] ?? '');
            $password = $params['password'] ?? '';
            $remember = (bool) ($params['remember'] ?? false);
            if (empty($username) || empty($password)) {
                return ResponseService::error('Username and password are required', null, 400);
            }
            return $this->processLogin($username, $password, $remember);
        } catch (\Exception $e) {
            return ResponseService::error(
                'Login failed',
                ['error' => $e->getMessage()],
                500,
            );
        }
    }
}
