"""Password authentication provider — stdlib only (PBKDF2)."""

from __future__ import annotations

import hashlib
import os

from waft_client.config import AuthConfig
from waft_client.models import ProviderInfo
from waft_client.session import create_session_token
from waft_client.store import AuthStore


def hash_password(password: str) -> str:
    """Hash a password with PBKDF2-SHA256 + random salt."""
    salt = os.urandom(32)
    key = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100_000)
    return salt.hex() + ":" + key.hex()


def verify_password(password: str, stored: str) -> bool:
    """Verify a password against a stored PBKDF2 hash."""
    try:
        salt_hex, key_hex = stored.split(":")
        salt = bytes.fromhex(salt_hex)
        key = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100_000)
        return key.hex() == key_hex
    except (ValueError, IndexError):
        return False


INFO = ProviderInfo(id="password", name="Email & Password", provider_type="password")


def handle_route(
    path: str,
    method: str,
    event: dict,
    store: AuthStore,
    config: AuthConfig,
) -> dict | None:
    """Handle /auth/password/* routes."""
    if path == "/auth/password/login" and method == "POST":
        return _handle_login(event, store, config)
    if path == "/auth/password/register" and method == "POST":
        return _handle_register(event, store, config)
    return None


def _parse_form(event: dict) -> dict[str, str]:
    """Parse URL-encoded form body from Lambda event."""
    import base64
    from urllib.parse import parse_qs

    body = event.get("body", "")
    if event.get("isBase64Encoded") and body:
        body = base64.b64decode(body).decode()
    params = parse_qs(body, keep_blank_values=True)
    return {k: v[0] for k, v in params.items()}


def _handle_login(event: dict, store: AuthStore, config: AuthConfig) -> dict:
    form = _parse_form(event)
    email = form.get("email", "").strip().lower()
    password = form.get("password", "")

    if not email or not password:
        return _login_error("Email and password are required", config)

    # Find user by email, then check password credential
    user = store.get_user_by_email(email)
    if not user:
        return _login_error("Invalid email or password", config)

    cred = store.get_credential(user.id, "password")
    if not cred:
        return _login_error("Invalid email or password", config)

    _, stored_hash = cred
    if not verify_password(password, stored_hash):
        return _login_error("Invalid email or password", config)

    # Issue session
    token = create_session_token(user.id, user.email, config.jwt_secret, config.cookie_max_age)
    return _redirect_with_cookie(config.after_login_path, config, token)


def _handle_register(event: dict, store: AuthStore, config: AuthConfig) -> dict:
    form = _parse_form(event)
    email = form.get("email", "").strip().lower()
    password = form.get("password", "")

    if not email or not password:
        return _register_error("Email and password are required", config)

    if len(password) < 8:
        return _register_error("Password must be at least 8 characters", config)

    # Check invite if invite_only mode
    if config.signup_mode == "invite_only" and not store.has_invite(email):
        return _register_error("Signup requires an invitation", config)

    # Check if user already exists
    if store.get_user_by_email(email):
        return _register_error("An account with this email already exists", config)

    # Create user and store password credential
    user = store.create_user(email)
    store.store_credential(user.id, "password", email, hash_password(password))

    if config.signup_mode == "invite_only":
        store.use_invite(email)

    # Issue session
    token = create_session_token(user.id, user.email, config.jwt_secret, config.cookie_max_age)
    return _redirect_with_cookie(config.after_login_path, config, token)


def _cookie_str(config: AuthConfig, token: str) -> str:
    parts = [
        f"{config.cookie_name}={token}",
        "Path=/",
        "HttpOnly",
        "SameSite=Lax",
        f"Max-Age={config.cookie_max_age}",
    ]
    if config.cookie_secure:
        parts.append("Secure")
    return "; ".join(parts)


def _redirect_with_cookie(path: str, config: AuthConfig, token: str) -> dict:
    return {
        "statusCode": 302,
        "headers": {"location": path},
        "cookies": [_cookie_str(config, token)],
        "body": "",
    }


def _login_error(msg: str, config: AuthConfig) -> dict:
    from waft_client.ui import login_page
    from waft_client.auth import _get_provider_infos

    html = login_page(_get_provider_infos(config), config.register_path, error=msg, signup_mode=config.signup_mode)
    return {"statusCode": 200, "headers": {"content-type": "text/html"}, "body": html}


def _register_error(msg: str, config: AuthConfig) -> dict:
    from waft_client.ui import register_page
    from waft_client.auth import _get_provider_infos

    html = register_page(_get_provider_infos(config), config.login_path, error=msg)
    return {"statusCode": 200, "headers": {"content-type": "text/html"}, "body": html}
