"""Dashboard API: web panel + CLI endpoints for managing accounts, VMs, and costs.

Mounted at /dashboard/ on the main proxy app.
"""

from __future__ import annotations

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel

from proxym.accounts import (
    Account,
    AccountCredentials,
    AccountLimits,
    AccountManager,
    ProviderType,
    ToolBinding,
    ToolType,
)
from proxym.config import get_settings
from proxym.providers import MODELS
from proxym.virt import VMConfig, VMOrchestrator

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

# Singletons (initialized on first use)
_acct_mgr: AccountManager | None = None
_vm_orch: VMOrchestrator | None = None


def _accounts() -> AccountManager:
    global _acct_mgr
    if _acct_mgr is None:
        _acct_mgr = AccountManager()
    return _acct_mgr


def _vms() -> VMOrchestrator:
    global _vm_orch
    if _vm_orch is None:
        _vm_orch = VMOrchestrator()
    return _vm_orch


# ── Pydantic request models ──────────────────────────────────

class CreateAccountReq(BaseModel):
    name: str
    provider: str
    email: str = ""
    plan_tier: str = ""
    api_key: str = ""
    tags: list[str] = []
    daily_budget_usd: float = 0
    monthly_budget_usd: float = 0


class BindToolReq(BaseModel):
    tool_type: str
    tool_name: str
    vm_id: str = ""
    project_path: str = ""
    browser_profile: str = ""


class CreateVMReq(BaseModel):
    name: str
    account_id: str = ""
    tool: str = ""
    vcpus: int = 2
    memory_mb: int = 4096
    disk_gb: int = 20
    base_image: str = "fedora-40-dev"
    code_mount_host: str = ""


class SwitchProjectReq(BaseModel):
    project_path: str


# ── Account endpoints ────────────────────────────────────────

@router.get("/accounts")
async def list_accounts(provider: str | None = None, tag: str | None = None):
    mgr = _accounts()
    prov = ProviderType(provider) if provider else None
    accts = mgr.list_accounts(provider=prov, tag=tag)
    return {"accounts": [a.status_summary() for a in accts]}


@router.post("/accounts")
async def create_account(req: CreateAccountReq):
    mgr = _accounts()
    acct = Account(
        name=req.name,
        provider=ProviderType(req.provider),
        email=req.email,
        plan_tier=req.plan_tier,
        credentials=AccountCredentials(api_key=req.api_key),
        limits=AccountLimits(
            user_daily_budget_usd=req.daily_budget_usd,
            user_monthly_budget_usd=req.monthly_budget_usd,
        ),
        tags=req.tags,
    )
    mgr.add_account(acct)
    return acct.status_summary()


@router.get("/accounts/{account_id}")
async def get_account(account_id: str):
    acct = _accounts().get_account(account_id)
    if not acct:
        raise HTTPException(404, "Account not found")
    return acct.status_summary()


@router.delete("/accounts/{account_id}")
async def delete_account(account_id: str):
    if _accounts().remove_account(account_id):
        return {"ok": True}
    raise HTTPException(404, "Account not found")


@router.post("/accounts/{account_id}/bind-tool")
async def bind_tool(account_id: str, req: BindToolReq):
    binding = ToolBinding(
        tool_type=ToolType(req.tool_type),
        tool_name=req.tool_name,
        vm_id=req.vm_id,
        project_path=req.project_path,
        browser_profile=req.browser_profile,
    )
    if _accounts().bind_tool(account_id, binding):
        return {"ok": True}
    raise HTTPException(404, "Account not found")


# ── Cost / usage endpoints ───────────────────────────────────

@router.get("/costs")
async def cost_summary():
    """Aggregate cost dashboard across all accounts."""
    return _accounts().get_cost_summary()


@router.get("/costs/detailed")
async def cost_detailed():
    """Per-account cost breakdown."""
    mgr = _accounts()
    accounts = mgr.list_accounts()
    rows = []
    for a in accounts:
        rows.append({
            "id": a.id,
            "name": a.name,
            "provider": a.provider.value,
            "plan": a.plan_tier,
            "daily_cost": round(a.usage.daily_cost_usd, 4),
            "monthly_cost": round(a.usage.total_cost_usd, 4),
            "daily_requests": a.usage.daily_requests,
            "total_requests": a.usage.total_requests,
            "budget": a.limits.budget_remaining(a.usage),
            "over_budget": a.is_over_budget,
        })
    return {"accounts": rows, "summary": mgr.get_cost_summary()}


# ── VM endpoints ─────────────────────────────────────────────

@router.get("/vms")
async def list_vms():
    return {"vms": [
        {
            "id": s.id, "name": s.name, "state": s.state.value,
            "account_id": s.account_id, "tool": s.tool,
            "uptime": s.uptime_seconds, "ip": s.ip_address,
            "code_mount": s.code_mount,
            # Resources
            "memory_mb": s.memory_mb,
            "vcpus": s.vcpus,
            "disk_gb": s.disk_gb,
            # Timing
            "last_started_at": s.last_started_at,
            "created_at": s.created_at,
        }
        for s in _vms().list_vms()
    ]}


@router.post("/vms")
async def create_vm(req: CreateVMReq):
    config = VMConfig(
        name=req.name,
        account_id=req.account_id,
        tool=req.tool,
        vcpus=req.vcpus,
        memory_mb=req.memory_mb,
        disk_gb=req.disk_gb,
        base_image=req.base_image,
        code_mount_host=req.code_mount_host,
    )
    vm = _vms().create_vm(config)
    return {"id": vm.id, "name": vm.name, "state": vm.state.value}


@router.post("/vms/{vm_id}/start")
async def start_vm(vm_id: str):
    vms = _vms()
    vm = vms.vms.get(vm_id)
    if not vm:
        raise HTTPException(404, f"VM '{vm_id}' not found")

    try:
        success = vms.start_vm(vm_id)
        if success:
            return {"ok": True, "vm_id": vm_id, "state": "running"}
        backend = getattr(vm, "backend", "unknown")
        raise HTTPException(
            500,
            f"Failed to start VM '{vm_id}' (backend={backend}). "
            "Check server logs for details.",
        )
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, f"Failed to start VM: {e}")


@router.post("/vms/{vm_id}/stop")
async def stop_vm(vm_id: str, force: bool = False):
    if _vms().stop_vm(vm_id, force=force):
        return {"ok": True}
    raise HTTPException(500, "Failed to stop VM")


@router.post("/vms/{vm_id}/pause")
async def pause_vm(vm_id: str):
    if _vms().pause_vm(vm_id):
        return {"ok": True}
    raise HTTPException(500, "Failed to pause VM")


@router.post("/vms/{vm_id}/resume")
async def resume_vm(vm_id: str):
    if _vms().resume_vm(vm_id):
        return {"ok": True}
    raise HTTPException(500, "Failed to resume VM")


@router.post("/vms/{vm_id}/snapshot")
async def snapshot_vm(vm_id: str, name: str = ""):
    if _vms().snapshot_vm(vm_id, name):
        return {"ok": True}
    raise HTTPException(500, "Failed to create snapshot")


@router.post("/vms/{vm_id}/switch-project")
async def switch_project(vm_id: str, req: SwitchProjectReq):
    if _vms().switch_project(vm_id, req.project_path):
        return {"ok": True}
    raise HTTPException(500, "Failed to switch project")


@router.delete("/vms/{vm_id}")
async def delete_vm(vm_id: str):
    if _vms().delete_vm(vm_id):
        return {"ok": True}
    raise HTTPException(404, "VM not found")


@router.post("/vms/{vm_id}/delete")
async def delete_vm_post(vm_id: str):
    """POST variant for single VM deletion (matches frontend convention)."""
    if _vms().delete_vm(vm_id):
        return {"ok": True}
    raise HTTPException(404, "VM not found")


@router.post("/vms/bulk-delete")
async def bulk_delete_vms(req: list[str]):
    """Delete multiple VMs by their IDs. Returns list of successfully deleted VM IDs."""
    deleted = []
    failed = []
    for vm_id in req:
        try:
            if _vms().delete_vm(vm_id):
                deleted.append(vm_id)
            else:
                failed.append(vm_id)
        except Exception:
            failed.append(vm_id)
    return {"deleted": deleted, "failed": failed, "total_requested": len(req)}


# ── System status ────────────────────────────────────────────

@router.get("/")
async def dashboard_root():
    """Dashboard root - redirects to system status."""
    return await system_status()

@router.get("/system")
async def system_status():
    """Full system overview: accounts + VMs + costs + libvirt + providers."""
    settings = get_settings()
    libvirt = VMOrchestrator.check_libvirt_available()
    configured = settings.available_providers()
    return {
        "accounts": _accounts().get_cost_summary(),
        "vms": {
            "total": len(_vms().vms),
            "running": sum(1 for v in _vms().vms.values() if v.state.value == "running"),
        },
        "libvirt": libvirt,
        "providers": {
            "configured": list(configured.keys()),
            "total_models": sum(
                1 for m in MODELS.values() if m.provider in configured
            ),
        },
    }


@router.get("/providers")
async def list_providers():
    """Show configured providers from .env and their model counts."""
    settings = get_settings()
    configured = settings.available_providers()
    providers = []
    for name, key in configured.items():
        model_count = sum(1 for m in MODELS.values() if m.provider == name)
        # Ollama doesn't require API key (local service)
        is_active = name == "ollama" or (name != "ollama" and bool(key))
        providers.append({
            "name": name,
            "configured": True,
            "has_key": is_active,
            "model_count": model_count,
        })
    return {"providers": providers}


@router.get("/logs")
async def get_logs(lines: int = 50, level: str | None = None):
    """Get recent log entries from proxym log file."""
    from pathlib import Path
    import time
    
    log_paths = [
        Path.home() / ".local/share/proxym/proxym.log",
        Path("/var/log/proxym/proxym.log"),
        Path("./proxym.log"),
    ]
    
    log_file = None
    for p in log_paths:
        if p.exists():
            log_file = p
            break
    
    if not log_file:
        return {"logs": [], "error": "No log file found"}
    
    try:
        with open(log_file, "r") as f:
            all_lines = f.readlines()
        
        # Get last N lines
        recent = all_lines[-lines:]
        
        logs = []
        for line in recent:
            line = line.strip()
            if not line:
                continue
            
            # Parse structured log lines (timestamp level message)
            parts = line.split(" ", 2)
            if len(parts) >= 2:
                timestamp = parts[0] if len(parts) > 0 else ""
                log_level = parts[1] if len(parts) > 1 else "INFO"
                message = parts[2] if len(parts) > 2 else line
            else:
                timestamp = time.strftime("%Y-%m-%dT%H:%M:%S")
                log_level = "INFO"
                message = line
            
            # Filter by level if specified
            if level and log_level.upper() != level.upper():
                continue
            
            logs.append({
                "timestamp": timestamp,
                "level": log_level.upper(),
                "message": message,
            })
        
        return {"logs": logs[-lines:], "source": str(log_file)}
    except Exception as e:
        return {"logs": [], "error": str(e)}
