"""Client-side logging for aablocks API calls.

Logs all API activity to ~/.aab/logs/<domain>/ with rolling log files.
"""

from __future__ import annotations

import base64
import json
import logging
import os
import sys
import time
from logging.handlers import RotatingFileHandler
from pathlib import Path
from urllib.parse import urlparse

from .cached_token import get_cached_token
from .config import get_auth_domain, get_base_url

LOG_DIR = Path("~/.aab/logs").expanduser()
MAX_BYTES = 10 * 1024 * 1024  # 10 MB per log file
BACKUP_COUNT = 5  # Keep 5 backup files

_loggers: dict[str, logging.Logger] = {}
_verbose: bool = False


def set_verbose(enabled: bool) -> None:
    """Enable or disable verbose console output."""
    global _verbose
    _verbose = enabled

    # Add or remove console handlers from existing loggers
    for logger in _loggers.values():
        _update_console_handler(logger)


def _get_domain_from_url(url: str) -> str:
    """Extract domain from URL for log directory naming."""
    parsed = urlparse(url)
    return parsed.netloc or "unknown"


def _get_username() -> str | None:
    """Extract email from the cached JWT token."""
    try:
        token_data = get_cached_token(get_auth_domain())
        if not token_data:
            return None

        id_token = token_data.get("id_token")
        if not id_token:
            return None

        # JWT is base64url encoded: header.payload.signature
        parts = id_token.split(".")
        if len(parts) != 3:
            return None

        # Decode payload (add padding if needed)
        payload = parts[1]
        padding = 4 - len(payload) % 4
        if padding != 4:
            payload += "=" * padding

        claims = json.loads(base64.urlsafe_b64decode(payload))
        return claims.get("email")
    except (json.JSONDecodeError, KeyError, ValueError, UnicodeDecodeError):
        # Malformed JWT - can't extract username
        return None


def _update_console_handler(logger: logging.Logger) -> None:
    """Add or remove console handler based on verbose setting."""
    # Remove existing console handlers
    for handler in logger.handlers[:]:
        if isinstance(handler, logging.StreamHandler) and handler.stream == sys.stderr:
            logger.removeHandler(handler)

    # Add console handler if verbose
    if _verbose:
        console_handler = logging.StreamHandler(sys.stderr)
        console_handler.setLevel(logging.INFO)
        formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s")
        formatter.converter = time.gmtime  # Use UTC
        console_handler.setFormatter(formatter)
        logger.addHandler(console_handler)


def _get_logger(domain: str) -> logging.Logger:
    """Get or create a logger for the given domain."""
    if domain in _loggers:
        return _loggers[domain]

    # Create log directory with restrictive permissions on Unix

    log_dir = LOG_DIR / domain
    log_dir.mkdir(parents=True, exist_ok=True)
    # Windows (i.e. `nt`) relies on user directory ACLs
    if os.name != "nt":
        os.chmod(LOG_DIR, 0o700)
        os.chmod(log_dir, 0o700)

    # Create logger
    logger = logging.getLogger(f"aablocks.{domain}")
    logger.setLevel(logging.INFO)
    logger.propagate = False  # Don't propagate to root logger

    # Remove existing handlers
    logger.handlers.clear()

    # Add rotating file handler
    log_file = log_dir / "activity.log"
    handler = RotatingFileHandler(
        log_file,
        maxBytes=MAX_BYTES,
        backupCount=BACKUP_COUNT,
        encoding="utf-8",
    )
    handler.setLevel(logging.INFO)

    # Format: timestamp (UTC) | level | message
    formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s")
    formatter.converter = time.gmtime  # Use UTC
    handler.setFormatter(formatter)

    logger.addHandler(handler)

    # Add console handler if verbose mode is enabled
    _update_console_handler(logger)

    _loggers[domain] = logger

    return logger


def log_request(
    method: str,
    endpoint: str,
    params: dict | None = None,
    status_code: int | None = None,
    error: str | None = None,
    latency_ms: int | None = None,
) -> None:
    """Log an API request.

    Args:
        method: HTTP method (GET, POST, etc.)
        endpoint: API endpoint path (e.g., /datasets)
        params: Query parameters
        status_code: HTTP response status code
        error: Error message if request failed
        latency_ms: Request latency in milliseconds
    """
    base_url = get_base_url()
    domain = _get_domain_from_url(base_url)
    logger = _get_logger(domain)

    # Build log message
    parts = [f"{method} {base_url}{endpoint}"]

    username = _get_username()
    if username:
        parts.append(f"user={username}")

    if params:
        param_str = "&".join(f"{k}={v}" for k, v in params.items())
        parts.append(f"params={param_str}")

    if status_code is not None:
        parts.append(f"status={status_code}")

    if latency_ms is not None:
        parts.append(f"latency={latency_ms}ms")

    if error:
        parts.append(f"error={error}")

    message = " | ".join(parts)

    if error or (status_code and status_code >= 400):
        logger.error(message)
    else:
        logger.info(message)


def log_auth(action: str, success: bool, error: str | None = None) -> None:
    """Log an authentication event.

    Args:
        action: Auth action (login, logout, refresh)
        success: Whether the action succeeded
        error: Error message if action failed
    """
    base_url = get_base_url()
    domain = _get_domain_from_url(base_url)
    logger = _get_logger(domain)

    status = "success" if success else "failed"
    username = _get_username()

    parts = [f"AUTH {action}", status]
    if username:
        parts.insert(1, f"user={username}")
    if error:
        parts.append(f"error={error}")

    message = " | ".join(parts)

    if success:
        logger.info(message)
    else:
        logger.error(message)


def log_error(traceback: str) -> None:
    """Log a full error traceback.

    Args:
        traceback: Full traceback string from traceback.format_exc()
    """
    base_url = get_base_url()
    domain = _get_domain_from_url(base_url)
    logger = _get_logger(domain)

    username = _get_username()
    user_part = f" | user={username}" if username else ""
    logger.error(f"EXCEPTION{user_part}\n{traceback}")
