from __future__ import annotations

import sys
import shutil
from typing import Any, Dict, List, Optional, Sequence, Tuple
from datetime import datetime

from cmdlet._shared import Cmdlet, CmdletArg
from SYS import pipeline as ctx
from SYS.result_table import ResultTable
from SYS.logger import log, set_debug, debug
from SYS.rich_display import stdout_console

CMDLET = Cmdlet(
    name=".status",
    summary="Check and display service/provider status",
    usage=".status",
    arg=[],
)

def _upper(value: Any) -> str:
    text = "" if value is None else str(value)
    return text.upper()

def _add_startup_check(
    table: ResultTable,
    status: str,
    name: str,
    *,
    provider: str = "",
    store: str = "",
    files: int | str | None = None,
    detail: str = "",
) -> None:
    row = table.add_row()
    row.add_column("STATUS", _upper(status))
    row.add_column("NAME", _upper(name))
    row.add_column("PROVIDER", _upper(provider or ""))
    row.add_column("STORE", _upper(store or ""))
    row.add_column("FILES", "" if files is None else str(files))
    row.add_column("DETAIL", _upper(detail or ""))

def _has_store_subtype(cfg: dict, subtype: str) -> bool:
    store_cfg = cfg.get("store")
    if not isinstance(store_cfg, dict):
        return False
    bucket = store_cfg.get(subtype)
    if not isinstance(bucket, dict):
        return False
    return any(isinstance(v, dict) and bool(v) for v in bucket.values())

def _has_provider(cfg: dict, name: str) -> bool:
    provider_cfg = cfg.get("provider")
    if not isinstance(provider_cfg, dict):
        return False
    block = provider_cfg.get(str(name).strip().lower())
    return isinstance(block, dict) and bool(block)

def _has_tool(cfg: dict, name: str) -> bool:
    tool_cfg = cfg.get("tool")
    if not isinstance(tool_cfg, dict):
        return False
    block = tool_cfg.get(str(name).strip().lower())
    return isinstance(block, dict) and bool(block)

def _ping_url(url: str, timeout: float = 3.0) -> tuple[bool, str]:
    try:
        from API.HTTP import HTTPClient
        with HTTPClient(timeout=timeout, retries=1) as client:
            resp = client.get(url, allow_redirects=True)
        code = int(getattr(resp, "status_code", 0) or 0)
        ok = 200 <= code < 500
        return ok, f"{url} (HTTP {code})"
    except Exception as exc:
        return False, f"{url} ({type(exc).__name__})"

def _provider_display_name(key: str) -> str:
    k = (key or "").strip()
    low = k.lower()
    if low == "openlibrary": return "OpenLibrary"
    if low == "alldebrid": return "AllDebrid"
    if low == "youtube": return "YouTube"
    return k[:1].upper() + k[1:] if k else "Provider"

def _default_provider_ping_targets(provider_key: str) -> list[str]:
    prov = (provider_key or "").strip().lower()
    if prov == "openlibrary": return ["https://openlibrary.org"]
    if prov == "youtube": return ["https://www.youtube.com"]
    if prov == "bandcamp": return ["https://bandcamp.com"]
    if prov == "libgen":
        try:
            from Provider.libgen import MIRRORS
            return [str(x).rstrip("/") + "/json.php" for x in (MIRRORS or []) if str(x).strip()]
        except ImportError: return []
    return []

def _ping_first(urls: list[str]) -> tuple[bool, str]:
    for u in urls:
        ok, detail = _ping_url(u)
        if ok: return True, detail
    if urls:
        ok, detail = _ping_url(urls[0])
        return ok, detail
    return False, "No ping target"

def _run(result: Any, args: List[str], config: Dict[str, Any]) -> int:
    startup_table = ResultTable(
        "*********<IGNITIO>*********<NOUSEMPEH>*********<RUGRAPOG>*********<OMEGHAU>*********"
    )
    startup_table.set_no_choice(True).set_preserve_order(True)
    startup_table.set_value_case("upper")

    debug_enabled = bool(config.get("debug", False))
    _add_startup_check(startup_table, "ENABLED" if debug_enabled else "DISABLED", "DEBUGGING")

    try:
        # MPV check
        try:
            from MPV.mpv_ipc import MPV
            MPV()
            mpv_path = shutil.which("mpv")
            _add_startup_check(startup_table, "ENABLED", "MPV", detail=mpv_path or "Available")
        except Exception as exc:
            _add_startup_check(startup_table, "DISABLED", "MPV", detail=str(exc))

        # Store Registry
        store_registry = None
        try:
            from Store import Store as StoreRegistry
            store_registry = StoreRegistry(config=config, suppress_debug=True)
        except Exception:
            pass

        # Hydrus
        if _has_store_subtype(config, "hydrusnetwork"):
            hcfg = config.get("store", {}).get("hydrusnetwork", {})
            for iname, icfg in hcfg.items():
                if not isinstance(icfg, dict): continue
                nkey = str(icfg.get("NAME") or iname)
                uval = str(icfg.get("URL") or "").strip()
                ok = bool(store_registry and store_registry.is_available(nkey))
                status = "ENABLED" if ok else "DISABLED"
                files = None
                detail = uval
                if ok and store_registry:
                    try:
                        backend = store_registry[nkey]
                        files = getattr(backend, "total_count", None)
                        if files is None and hasattr(backend, "get_total_count"):
                            files = backend.get_total_count()
                    except Exception: pass
                else:
                    err = store_registry.get_backend_error(iname) if store_registry else None
                    detail = f"{uval} - {err or 'Unavailable'}"
                _add_startup_check(startup_table, status, nkey, store="hydrusnetwork", files=files, detail=detail)

        # Providers
        pcfg = config.get("provider", {})
        if isinstance(pcfg, dict) and pcfg:
            from ProviderCore.registry import list_providers, list_search_providers, list_file_providers
            from Provider.metadata_provider import list_metadata_providers
            
            p_avail = list_providers(config) or {}
            s_avail = list_search_providers(config) or {}
            f_avail = list_file_providers(config) or {}
            m_avail = list_metadata_providers(config) or {}
            
            already = {"matrix"}
            for pname in pcfg.keys():
                prov = str(pname).lower()
                if prov in already: continue
                display = _provider_display_name(prov)
                
                if prov == "alldebrid":
                    try:
                        from Provider.alldebrid import _get_debrid_api_key
                        from API.alldebrid import AllDebridClient
                        api_key = _get_debrid_api_key(config)
                        if not api_key:
                            _add_startup_check(startup_table, "DISABLED", display, provider=prov, detail="Not configured")
                        else:
                            client = AllDebridClient(api_key)
                            _add_startup_check(startup_table, "ENABLED", display, provider=prov, detail=getattr(client, "base_url", "Connected"))
                    except Exception as exc:
                        _add_startup_check(startup_table, "DISABLED", display, provider=prov, detail=str(exc))
                    already.add(prov)
                    continue

                is_known = prov in p_avail or prov in s_avail or prov in f_avail or prov in m_avail
                if not is_known:
                    _add_startup_check(startup_table, "UNKNOWN", display, provider=prov, detail="Not registered")
                else:
                    ok_val = p_avail.get(prov) or s_avail.get(prov) or f_avail.get(prov) or m_avail.get(prov)
                    detail = "Configured" if ok_val else "Not configured"
                    ping_targets = _default_provider_ping_targets(prov)
                    if ping_targets:
                        pok, pdet = _ping_first(ping_targets)
                        detail = pdet if ok_val else f"{detail} | {pdet}"
                    _add_startup_check(startup_table, "ENABLED" if ok_val else "DISABLED", display, provider=prov, detail=detail)
                already.add(prov)

        # Matrix
        if _has_provider(config, "matrix"):
            try:
                from Provider.matrix import Matrix
                m_prov = Matrix(config)
                mcfg = config.get("provider", {}).get("matrix", {})
                hs = str(mcfg.get("homeserver") or "").strip()
                rid = str(mcfg.get("room_id") or "").strip()
                detail = f"{hs} room:{rid}"
                _add_startup_check(startup_table, "ENABLED" if m_prov.validate() else "DISABLED", "Matrix", provider="matrix", detail=detail)
            except Exception as exc:
                _add_startup_check(startup_table, "DISABLED", "Matrix", provider="matrix", detail=str(exc))

        # Folders
        if _has_store_subtype(config, "folder"):
            fcfg = config.get("store", {}).get("folder", {})
            for iname, icfg in fcfg.items():
                if not isinstance(icfg, dict): continue
                nkey = str(icfg.get("NAME") or iname)
                pval = str(icfg.get("PATH") or icfg.get("path") or "").strip()
                ok = bool(store_registry and store_registry.is_available(nkey))
                if ok and store_registry:
                    backend = store_registry[nkey]
                    scan_ok = getattr(backend, "scan_ok", True)
                    sdet = getattr(backend, "scan_detail", "Up to date")
                    stats = getattr(backend, "scan_stats", {})
                    files = int(stats.get("files_total_db", 0)) if stats else None
                    _add_startup_check(startup_table, "SCANNED" if scan_ok else "ERROR", nkey, store="folder", files=files, detail=f"{pval} - {sdet}")
                else:
                    err = store_registry.get_backend_error(iname) if store_registry else None
                    _add_startup_check(startup_table, "ERROR", nkey, store="folder", detail=f"{pval} - {err or 'Unavailable'}")

        # Cookies
        try:
            from tool.ytdlp import YtDlpTool
            cf = YtDlpTool(config).resolve_cookiefile()
            _add_startup_check(startup_table, "FOUND" if cf else "MISSING", "Cookies", detail=str(cf) if cf else "Not found")
        except Exception: pass

    except Exception as exc:
        debug(f"Status check failed: {exc}")

    if startup_table.rows:
        # Mark as rendered to prevent CLI.py from auto-printing it to stdout 
        # (avoiding duplication in TUI logs, while keeping it in TUI Results)
        setattr(startup_table, "_rendered_by_cmdlet", True)
        ctx.set_current_stage_table(startup_table)
    
    return 0

CMDLET.exec = _run
