"""
Performance monitoring router: health checks for all external services and data pipelines.
"""
import os
import time
import logging
import asyncio
from datetime import datetime, timedelta
from typing import Dict, Tuple

import httpx
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from sqlalchemy import text

from app.database import get_db
from app.models import User, SyncMetadata
from app.routers.auth import require_permission
from app.config import settings
from app.config.xero_config import is_xero_enabled
from app.services.simpro_api_limits import SIMPRO_API_CACHE_TTL_SECONDS

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/performance", tags=["performance"])

STATUS_OK = "ok"
STATUS_DEGRADED = "degraded"
STATUS_DOWN = "down"
STATUS_UNCONFIGURED = "unconfigured"

# Simpro GET /companies/0/ reachability checks — same TTL as other Simpro caches
_simpro_reachability_cache: Dict[str, Tuple[float, dict]] = {}


async def _check_url(url: str, timeout: float = 10.0) -> dict:
    """HTTP GET a URL and return status + response time."""
    start = time.monotonic()
    try:
        async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
            resp = await client.get(url)
            elapsed_ms = round((time.monotonic() - start) * 1000)
            ok = 200 <= resp.status_code < 400
            return {
                "status": STATUS_OK if ok else STATUS_DEGRADED,
                "status_code": resp.status_code,
                "response_ms": elapsed_ms,
            }
    except httpx.TimeoutException:
        elapsed_ms = round((time.monotonic() - start) * 1000)
        return {"status": STATUS_DOWN, "error": "Timeout", "response_ms": elapsed_ms}
    except Exception as e:
        elapsed_ms = round((time.monotonic() - start) * 1000)
        return {"status": STATUS_DOWN, "error": str(e)[:120], "response_ms": elapsed_ms}


async def _check_xero() -> dict:
    """Verify Xero API connectivity by attempting a token refresh check."""
    if not is_xero_enabled():
        return {"status": STATUS_UNCONFIGURED, "message": "Xero credentials not configured"}
    start = time.monotonic()
    try:
        from app.services.xero_service import xero_service
        token = xero_service._get_access_token()
        elapsed_ms = round((time.monotonic() - start) * 1000)
        if token:
            return {"status": STATUS_OK, "response_ms": elapsed_ms, "message": "Token valid"}
        return {"status": STATUS_DOWN, "response_ms": elapsed_ms, "message": "No token returned"}
    except Exception as e:
        elapsed_ms = round((time.monotonic() - start) * 1000)
        return {"status": STATUS_DOWN, "response_ms": elapsed_ms, "error": str(e)[:120]}


async def _check_simpro_branch(branch_id: str) -> dict:
    """Check a single simPRO branch API is reachable (GET companies/0/). Result cached per branch."""
    branch = settings.BRANCHES.get(branch_id)
    if not branch:
        return {"status": STATUS_UNCONFIGURED, "message": f"Branch {branch_id} not found"}
    api_key = branch.get("api_key", "")
    api_url = branch.get("api_url", "")
    if not api_key or not api_url:
        return {"status": STATUS_UNCONFIGURED, "message": "API key or URL not set"}

    now = time.time()
    cached = _simpro_reachability_cache.get(branch_id)
    if cached:
        ts, payload = cached
        if now - ts < SIMPRO_API_CACHE_TTL_SECONDS:
            return dict(payload)

    start = time.monotonic()
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            resp = await client.get(
                f"{api_url}/companies/0/",
                headers={"Authorization": f"Bearer {api_key}", "Accept": "application/json"},
            )
            elapsed_ms = round((time.monotonic() - start) * 1000)
            if resp.status_code in (200, 404, 403):
                result = {"status": STATUS_OK, "response_ms": elapsed_ms, "status_code": resp.status_code}
            else:
                result = {"status": STATUS_DEGRADED, "response_ms": elapsed_ms, "status_code": resp.status_code}
    except Exception as e:
        elapsed_ms = round((time.monotonic() - start) * 1000)
        result = {"status": STATUS_DOWN, "response_ms": elapsed_ms, "error": str(e)[:120]}

    _simpro_reachability_cache[branch_id] = (time.time(), dict(result))
    return result


async def _check_sendgrid() -> dict:
    """Verify SendGrid API key is valid by hitting their /v3/user/credits endpoint."""
    api_key = os.getenv("SENDGRID_API_KEY", "").strip()
    if not api_key:
        return {"status": STATUS_UNCONFIGURED, "message": "SENDGRID_API_KEY not set"}

    start = time.monotonic()
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            resp = await client.get(
                "https://api.sendgrid.com/v3/user/credits",
                headers={"Authorization": f"Bearer {api_key}", "Accept": "application/json"},
            )
            elapsed_ms = round((time.monotonic() - start) * 1000)
            if resp.status_code == 200:
                data = resp.json()
                return {
                    "status": STATUS_OK,
                    "response_ms": elapsed_ms,
                    "credits_remaining": data.get("remain"),
                    "credits_total": data.get("total"),
                }
            if resp.status_code == 401:
                return {"status": STATUS_DOWN, "response_ms": elapsed_ms, "error": "Invalid API key"}
            return {"status": STATUS_DEGRADED, "response_ms": elapsed_ms, "status_code": resp.status_code}
    except Exception as e:
        elapsed_ms = round((time.monotonic() - start) * 1000)
        return {"status": STATUS_DOWN, "response_ms": elapsed_ms, "error": str(e)[:120]}


async def _check_database(db: Session) -> dict:
    """Verify database connectivity."""
    start = time.monotonic()
    try:
        db.execute(text("SELECT 1"))
        elapsed_ms = round((time.monotonic() - start) * 1000)
        return {"status": STATUS_OK, "response_ms": elapsed_ms}
    except Exception as e:
        elapsed_ms = round((time.monotonic() - start) * 1000)
        return {"status": STATUS_DOWN, "response_ms": elapsed_ms, "error": str(e)[:120]}


def _check_data_freshness(db: Session) -> dict:
    """Check how recently data was synced/updated."""
    result = {"status": STATUS_OK, "checks": {}}

    last_sync = db.query(SyncMetadata).order_by(SyncMetadata.last_sync_time.desc()).first()
    if last_sync:
        age_minutes = (datetime.utcnow() - last_sync.last_sync_time).total_seconds() / 60
        sync_info = {
            "last_sync": last_sync.last_sync_time.isoformat(),
            "age_minutes": round(age_minutes, 1),
            "status": STATUS_OK if age_minutes < 60 else (STATUS_DEGRADED if age_minutes < 360 else STATUS_DOWN),
        }
        result["checks"]["invoice_sync"] = sync_info
        if sync_info["status"] != STATUS_OK:
            result["status"] = sync_info["status"]
    else:
        result["checks"]["invoice_sync"] = {"status": STATUS_UNCONFIGURED, "message": "No sync history"}

    try:
        from app.services.xero_cache import xero_cache
        stats = xero_cache.get_stats()
        result["checks"]["xero_cache"] = {
            "status": STATUS_OK,
            "size": stats.get("size", 0),
            "hit_rate": stats.get("hit_rate", 0),
            "hits": stats.get("hits", 0),
            "misses": stats.get("misses", 0),
        }
    except Exception:
        result["checks"]["xero_cache"] = {"status": STATUS_DEGRADED, "message": "Could not read cache stats"}

    return result


@router.get("")
async def get_performance_status(
    user: User = Depends(require_permission("performance.access")),
    db: Session = Depends(get_db),
):
    """
    Comprehensive health check of all external services and data pipelines.
    Returns status for: dashboard, wordpress, xero, simpro (per branch), sendgrid, database, data freshness.
    """
    wordpress_url = os.getenv("WORDPRESS_URL", "https://nixonlive.com.au")
    dashboard_url = os.getenv("APP_URL", "https://nixonstats.info")

    dashboard_task = _check_url(dashboard_url)
    wordpress_task = _check_url(wordpress_url)
    xero_task = _check_xero()
    sendgrid_task = _check_sendgrid()
    simpro_tasks = {bid: _check_simpro_branch(bid) for bid in settings.BRANCHES}

    dashboard_result, wordpress_result, xero_result, sendgrid_result = await asyncio.gather(
        dashboard_task, wordpress_task, xero_task, sendgrid_task
    )

    simpro_results = {}
    simpro_gathered = await asyncio.gather(*simpro_tasks.values())
    for bid, result in zip(simpro_tasks.keys(), simpro_gathered):
        branch_name = settings.BRANCHES[bid]["name"]
        simpro_results[branch_name] = result

    db_result = await asyncio.to_thread(_check_database, db)
    freshness = _check_data_freshness(db)

    all_statuses = [
        dashboard_result.get("status"),
        wordpress_result.get("status"),
        xero_result.get("status"),
        sendgrid_result.get("status"),
        db_result.get("status"),
        freshness.get("status"),
        *[r.get("status") for r in simpro_results.values()],
    ]

    filtered = [s for s in all_statuses if s and s != STATUS_UNCONFIGURED]
    if STATUS_DOWN in filtered:
        overall = STATUS_DOWN
    elif STATUS_DEGRADED in filtered:
        overall = STATUS_DEGRADED
    else:
        overall = STATUS_OK

    return {
        "overall_status": overall,
        "checked_at": datetime.utcnow().isoformat(),
        "services": {
            "dashboard": {"name": "Nixon Stats Dashboard", "url": dashboard_url, **dashboard_result},
            "wordpress": {"name": "Nixon Live Website", "url": wordpress_url, **wordpress_result},
            "xero": {"name": "Xero API", **xero_result},
            "sendgrid": {"name": "SendGrid Email", **sendgrid_result},
            "database": {"name": "Database", **db_result},
        },
        "simpro": {name: {"name": f"simPRO {name}", **data} for name, data in simpro_results.items()},
        "data_freshness": freshness,
    }
