import hmac
import hashlib
import json
import base64
from datetime import datetime, timedelta, timezone
from typing import Any

from src.config import Settings


def _base64url_encode(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")


def _base64url_decode(data: str) -> bytes:
    padding = 4 - len(data) % 4
    if padding != 4:
        data += "=" * padding
    return base64.urlsafe_b64decode(data)


def create_session_cookie(*, settings: Settings, user_id: str) -> str:
    """Create a signed session cookie value."""
    payload = {
        "user_id": user_id,
        "exp": int((datetime.now(timezone.utc) + timedelta(hours=settings.session_ttl_hours)).timestamp()),
    }
    
    header = _base64url_encode(json.dumps({"alg": "HS256", "typ": "session"}).encode())
    payload_encoded = _base64url_encode(json.dumps(payload).encode())
    message = f"{header}.{payload_encoded}"
    
    signature = hmac.new(
        settings.session_secret.encode(),
        message.encode(),
        hashlib.sha256,
    ).digest()
    signature_encoded = _base64url_encode(signature)
    
    return f"{message}.{signature_encoded}"


def decode_session_cookie(*, settings: Settings, cookie_value: str) -> dict[str, Any]:
    """Decode and verify a session cookie. Returns payload or raises ValueError."""
    parts = cookie_value.split(".")
    if len(parts) != 3:
        raise ValueError("invalid session format")
    
    header, payload_encoded, signature_encoded = parts
    message = f"{header}.{payload_encoded}"
    
    # Verify signature
    expected_signature = hmac.new(
        settings.session_secret.encode(),
        message.encode(),
        hashlib.sha256,
    ).digest()
    expected_signature_encoded = _base64url_encode(expected_signature)
    
    if not hmac.compare_digest(signature_encoded, expected_signature_encoded):
        raise ValueError("invalid session signature")
    
    # Decode payload
    payload_bytes = _base64url_decode(payload_encoded)
    payload = json.loads(payload_bytes)
    
    # Check expiry
    if payload.get("exp", 0) < int(datetime.now(timezone.utc).timestamp()):
        raise ValueError("session expired")
    
    return payload
