<?php

namespace PloverCRM\Core\Api\V1;

use PloverCRM\Common\StandalonePasswordManager;
use PloverCRM\Core\Api\AbstractEndpoint;
use PloverCRM\Core\Services\CurrentUserPayloadBuilder;
use WP_REST_Request;

/**
 * Standalone username/password authentication and access recovery.
 *
 * POST /wp-json/plover-crm/v1/auth/login           - Authenticate with username + password
 * POST /wp-json/plover-crm/v1/auth/access-recovery - Email a recovery link
 * GET  /wp-json/plover-crm/v1/auth/recover-access  - Show recovery page
 * POST /wp-json/plover-crm/v1/auth/recover-access  - Regenerate a password
 */
class AuthEndpoint extends AbstractEndpoint
{
    public function getRoute(): string
    {
        return '/auth/(?P<action>[a-z-]+)';
    }

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

    /**
     * No authentication required for auth endpoints.
     */
    public function checkPermission(WP_REST_Request $request)
    {
        return true;
    }

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

        if ($action === 'login' && $method === 'POST') {
            return $this->handleLogin($request);
        }

        if ($action === 'access-recovery' && $method === 'POST') {
            return $this->handleAccessRecovery($request);
        }

        if ($action === 'recover-access' && $method === 'GET') {
            return $this->handleRecoverAccessPage($request);
        }

        if ($action === 'recover-access' && $method === 'POST') {
            return $this->handleRecoverAccessConfirm($request);
        }

        return $this->error('Invalid auth action', 400, 'invalid_action');
    }

    /**
     * Authenticate a standalone client with username and password.
     */
    private function handleLogin(WP_REST_Request $request)
    {
        $data = (array) $request->get_json_params();
        $username = sanitize_user((string) ($data['username'] ?? ''), true);
        $password = (string) ($data['password'] ?? '');

        if ($username === '') {
            return $this->error('Username is required', 400, 'missing_username');
        }

        if ($password === '') {
            return $this->error('Password is required', 400, 'missing_password');
        }

        if (StandalonePasswordManager::isLoginRateLimited($username)) {
            return $this->error('Too many sign-in attempts. Please try again shortly.', 429, 'rate_limit_exceeded');
        }

        $auth = StandalonePasswordManager::validateCredentials($username, $password);

        if (!$auth) {
            return $this->error('Invalid username or password', 401, 'invalid_credentials');
        }

        $user = get_user_by('ID', (int) $auth['user_id']);

        if (!$user) {
            return $this->error('This account is no longer available', 401, 'invalid_credentials');
        }

        return $this->success([
            'user' => CurrentUserPayloadBuilder::build($user),
        ]);
    }

    /**
     * Email a one-time recovery link that can replace the current password.
     */
    private function handleAccessRecovery(WP_REST_Request $request)
    {
        $data = (array) $request->get_json_params();
        $email = trim((string) ($data['email'] ?? ''));

        if ($email === '') {
            return $this->error('Email is required', 400, 'missing_email');
        }

        if (!is_email($email)) {
            return $this->error('Invalid email format', 400, 'invalid_email');
        }

        $result = StandalonePasswordManager::sendRecoveryLink($email);

        if (($result['status'] ?? '') === StandalonePasswordManager::RECOVERY_RESULT_FAILED) {
            return $this->error('We could not send a recovery link right now. Please try again in a few minutes.', 500, 'recovery_delivery_failed');
        }

        return $this->success([
            'message' => 'If an account exists with this email and has CRM access, a secure recovery link will be sent or was sent recently. Please check your inbox.',
        ]);
    }

    /**
     * Render the secure recovery page.
     */
    private function handleRecoverAccessPage(WP_REST_Request $request)
    {
        $token = trim((string) $request->get_param('token'));

        if ($token === '') {
            return $this->renderErrorPage('Invalid or missing recovery token.');
        }

        $status = StandalonePasswordManager::getRecoveryTokenStatus($token);

        if ($status !== StandalonePasswordManager::RECOVERY_STATUS_VALID) {
            return $this->renderErrorPage(StandalonePasswordManager::getRecoveryTokenMessage($status));
        }

        return $this->renderConfirmationPage($token);
    }

    /**
     * Consume the recovery token and regenerate the user's password.
     */
    private function handleRecoverAccessConfirm(WP_REST_Request $request)
    {
        $data = (array) $request->get_json_params();
        $token = trim((string) ($data['token'] ?? ''));

        if ($token === '') {
            return $this->error('Invalid or missing recovery token.', 400, 'invalid_token');
        }

        $issued = StandalonePasswordManager::regeneratePasswordFromRecovery($token);

        if (is_wp_error($issued)) {
            $status = $issued->get_error_code() === 'password_generation_failed' ? 500 : 400;

            return $this->error($issued->get_error_message(), $status, (string) $issued->get_error_code());
        }

        return $this->success([
            'password' => $issued['password'],
        ]);
    }

    /**
     * Show the recovery confirmation UI.
     */
    private function renderConfirmationPage(string $token)
    {
        $siteName = esc_html(get_bloginfo('name'));

        header('Content-Type: text/html; charset=UTF-8');
        echo '<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reset Password - ' . $siteName . '</title>
    <style>
        * { box-sizing: border-box; }
        body {
            margin: 0;
            min-height: 100vh;
            display: grid;
            place-items: center;
            padding: 24px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
            background:
                radial-gradient(circle at top, rgba(56, 189, 87, 0.18), transparent 36%),
                linear-gradient(135deg, #f5f8f2 0%, #eef6ea 55%, #f9fbf5 100%);
            color: #16301d;
        }
        .card {
            width: min(100%, 560px);
            background: rgba(255, 255, 255, 0.92);
            border: 1px solid rgba(22, 48, 29, 0.08);
            border-radius: 28px;
            padding: 36px;
            box-shadow: 0 24px 60px rgba(22, 48, 29, 0.12);
        }
        .eyebrow {
            display: inline-flex;
            align-items: center;
            gap: 8px;
            padding: 8px 12px;
            border-radius: 999px;
            background: rgba(34, 197, 94, 0.12);
            color: #166534;
            font-size: 12px;
            font-weight: 700;
            letter-spacing: 0.08em;
            text-transform: uppercase;
        }
        h1 {
            margin: 18px 0 12px;
            font-size: clamp(30px, 5vw, 40px);
            line-height: 1.05;
        }
        p {
            margin: 0;
            line-height: 1.65;
            color: #486052;
        }
        .panel {
            margin-top: 28px;
            padding: 18px;
            border-radius: 20px;
            background: #f6fbf4;
            border: 1px solid rgba(22, 48, 29, 0.08);
        }
        .password {
            margin-top: 14px;
            display: none;
            gap: 12px;
            align-items: center;
            justify-content: space-between;
            padding: 16px 18px;
            border-radius: 18px;
            background: #16301d;
            color: #f7fff7;
            font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
            word-break: break-word;
        }
        .password.show {
            display: flex;
        }
        .password code {
            flex: 1;
            font-size: 13px;
            white-space: pre-wrap;
        }
        .actions {
            margin-top: 24px;
            display: flex;
            flex-wrap: wrap;
            gap: 12px;
        }
        button {
            appearance: none;
            border: 0;
            border-radius: 16px;
            padding: 14px 18px;
            font-size: 15px;
            font-weight: 700;
            cursor: pointer;
            transition: transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
        }
        button:hover { transform: translateY(-1px); }
        button:disabled {
            opacity: 0.7;
            cursor: not-allowed;
            transform: none;
        }
        .primary {
            background: linear-gradient(135deg, #177245 0%, #1f9d57 100%);
            color: #fff;
            box-shadow: 0 14px 30px rgba(31, 157, 87, 0.22);
        }
        .secondary {
            background: #fff;
            color: #16301d;
            border: 1px solid rgba(22, 48, 29, 0.12);
        }
        .warning {
            margin-top: 18px;
            padding: 14px 16px;
            border-radius: 16px;
            background: rgba(245, 158, 11, 0.12);
            color: #8a4b00;
            display: none;
        }
        .warning.show {
            display: block;
        }
        .status {
            margin-top: 18px;
            font-size: 14px;
            color: #486052;
        }
        .footer {
            margin-top: 26px;
            font-size: 13px;
            color: #6a7f70;
        }
        @media (max-width: 640px) {
            .card { padding: 28px 22px; }
            .password.show { flex-direction: column; align-items: stretch; }
        }
    </style>
</head>
<body>
    <main class="card">
        <div class="eyebrow">PloverCRM Password Reset</div>
        <h1>Generate a fresh password</h1>
        <p id="message">Create a new password for the mobile app and web app. It replaces the current password immediately, so save it before closing this page.</p>

        <section class="panel">
            <strong>What happens next</strong>
            <p style="margin-top: 10px;">We will replace the current password for this account. Anyone using the mobile app or web app with the old password will need the new one.</p>
            <div class="password" id="passwordBox">
                <code id="passwordValue"></code>
                <button type="button" class="secondary" id="copyBtn" onclick="copyPassword()">Copy</button>
            </div>
            <div class="warning" id="warning">This password is shown only once on this recovery page. Copy it somewhere safe before leaving.</div>
        </section>

        <div class="actions">
            <button type="button" class="primary" id="issueBtn" onclick="issuePassword()">Generate password</button>
        </div>
        <div class="status" id="status">Recovery link valid for 1 hour.</div>
        <div class="footer">' . $siteName . '</div>
    </main>

    <script>
        const recoveryToken = "' . esc_js($token) . '";

        async function issuePassword() {
            const button = document.getElementById("issueBtn");
            const status = document.getElementById("status");
            const message = document.getElementById("message");
            button.disabled = true;
            button.textContent = "Generating...";

            try {
                const response = await fetch(window.location.href, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify({ token: recoveryToken })
                });

                const payload = await response.json();

                if (!payload.success || !payload.data || !payload.data.password) {
                    throw new Error(payload.message || "Could not generate a new password.");
                }

                document.getElementById("passwordValue").textContent = payload.data.password;
                document.getElementById("passwordBox").classList.add("show");
                document.getElementById("warning").classList.add("show");
                message.textContent = "Your new password is ready. Copy it now and use it to sign in.";
                status.textContent = "Password generated successfully.";
                button.style.display = "none";
            } catch (error) {
                button.disabled = false;
                button.textContent = "Generate password";
                status.textContent = "Recovery failed. " + (error && error.message ? error.message : "Please request a new link.");
            }
        }

        async function copyPassword() {
            const password = document.getElementById("passwordValue").textContent;
            const copyBtn = document.getElementById("copyBtn");

            try {
                await navigator.clipboard.writeText(password);
                const original = copyBtn.textContent;
                copyBtn.textContent = "Copied";
                setTimeout(function () {
                    copyBtn.textContent = original;
                }, 1800);
            } catch (error) {
                copyBtn.textContent = "Copy failed";
            }
        }
    </script>
</body>
</html>';
        exit;
    }

    /**
     * Show a friendly recovery error page.
     */
    private function renderErrorPage(string $message)
    {
        $siteName = esc_html(get_bloginfo('name'));

        header('Content-Type: text/html; charset=UTF-8');
        echo '<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reset Password - ' . $siteName . '</title>
    <style>
        * { box-sizing: border-box; }
        body {
            margin: 0;
            min-height: 100vh;
            display: grid;
            place-items: center;
            padding: 24px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
            background: linear-gradient(135deg, #fff8f1 0%, #fdf2f2 100%);
            color: #3c1818;
        }
        .card {
            width: min(100%, 520px);
            background: rgba(255, 255, 255, 0.94);
            border: 1px solid rgba(140, 46, 46, 0.08);
            border-radius: 28px;
            padding: 34px;
            box-shadow: 0 24px 60px rgba(140, 46, 46, 0.12);
        }
        h1 {
            margin: 0 0 12px;
            font-size: clamp(28px, 4vw, 38px);
        }
        p {
            margin: 0;
            line-height: 1.65;
            color: #7a4a4a;
        }
        .note {
            margin-top: 18px;
            padding: 16px;
            border-radius: 16px;
            background: rgba(239, 68, 68, 0.08);
            color: #9a3131;
        }
        .footer {
            margin-top: 24px;
            font-size: 13px;
            color: #9b6b6b;
        }
    </style>
</head>
<body>
    <main class="card">
        <h1>Recovery link unavailable</h1>
        <p>' . esc_html($message) . '</p>
        <div class="note">Request a fresh reset link from the PloverCRM sign-in screen or the mobile app.</div>
        <div class="footer">' . $siteName . '</div>
    </main>
</body>
</html>';
        exit;
    }
}
