"""Doctor command registration."""

from __future__ import annotations

import json
import platform
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

import click

from g_gremlin import __version__
from g_gremlin.cli import (
    EXIT_USAGE,
    _emit_json_payload,
    _parse_iso8601,
    cli,
)
from g_gremlin import cli as cli_mod


@cli.command("doctor")
@click.argument("spreadsheet_id", required=False)
@click.option(
    "--live",
    "live_ping",
    is_flag=True,
    help="Perform a live Sheets API ping (requires SPREADSHEET_ID).",
)
@click.option(
    "--sfdc",
    "check_sfdc",
    is_flag=True,
    help="Check Salesforce CLI availability and auth.",
)
@click.option(
    "--format",
    "output_format",
    type=click.Choice(["text", "json"], case_sensitive=False),
    default="text",
    show_default=True,
    help="Output format for the doctor report.",
)
@click.option(
    "--json",
    "json_flag",
    is_flag=True,
    help="Alias for --format json.",
)
@click.option(
    "--output",
    "output_path",
    type=click.Path(dir_okay=False, path_type=Path),
    help="Write report to a file (use - for stdout).",
)
def doctor(
    spreadsheet_id: str | None,
    live_ping: bool,
    check_sfdc: bool,
    output_format: str,
    json_flag: bool,
    output_path: Path | None,
) -> None:
    """
    Diagnose environment, auth, and connectivity for g-gremlin.

    Checks (read-only):
      - CLI version, Python version, OS
      - Config discovery (g_gremlin.yaml if present)
      - Google OAuth credentials + token scopes/age
      - Sheets API ping (with --live and sheet id)
      - Salesforce CLI availability/auth (with --sfdc)

    Exit codes:
      0 = all checks passed
      2 = missing deps/auth/misconfig (actionable issues)
    """
    output_format = "json" if json_flag else output_format
    checks: list[dict[str, Any]] = []
    license_result = None

    def add_check(name: str, status: str, detail: str, fix: str | None = None, meta: dict[str, Any] | None = None) -> None:
        entry: dict[str, Any] = {"name": name, "status": status, "detail": detail}
        if fix:
            entry["fix"] = fix
        if meta:
            entry["meta"] = meta
        checks.append(entry)

    # Environment basics
    python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
    platform_str = f"{platform.system()} {platform.release()}"
    add_check(
        "environment",
        "ok",
        f"Python {python_version} on {platform_str}",
        meta={"python_version": python_version, "platform": platform_str, "cli_version": __version__},
    )

    # FoundryOps license (best-effort, cached)
    try:
        license_mod = cli_mod._get_license()
        license_result = license_mod.check_license()
        if license_result.valid and license_result.license:
            lic = license_result.license
            days_msg = f", {lic.days_remaining} days remaining" if lic.days_remaining is not None else ""
            add_check(
                "license",
                "ok",
                f"License active ({lic.tier}{days_msg})",
                meta={
                    "tier": lic.tier,
                    "status": lic.status,
                    "trial_ends_at": lic.trial_ends_at,
                    "days_remaining": lic.days_remaining,
                    "token_expires_at": lic.token_expires_at,
                },
            )
        else:
            add_check(
                "license",
                "warn",
                license_result.error if license_result else "No license information available.",
                fix="Run: g-gremlin auth login --token <TOKEN>",
            )
    except Exception as exc:
        add_check(
            "license",
            "warn",
            f"License check failed: {exc}",
        )

    # Config discovery (best-effort)
    cfg_candidates = [
        Path("g_gremlin.yaml"),
        Path("g_gremlin.yml"),
        Path(".g_gremlin.yaml"),
        Path(".g_gremlin") / "g_gremlin.yaml",
        Path(".g_gremlin") / "config.yaml",
    ]
    found_cfg = [str(p) for p in cfg_candidates if p.exists()]
    if found_cfg:
        add_check("config", "ok", f"Found config file(s): {', '.join(found_cfg)}", meta={"paths": found_cfg})
    else:
        add_check(
            "config",
            "warn",
            "No g_gremlin.yaml found in current directory.",
            fix="Create g_gremlin.yaml or pass --config to sync.",
        )

    # Google credentials + token
    google_mod = cli_mod._get_google_client()

    try:
        credentials_path = google_mod._get_credentials_path()
        add_check(
            "google_credentials",
            "ok",
            f"Client credentials found at {credentials_path}",
            meta={"credentials_path": str(credentials_path)},
        )
    except Exception as exc:
        add_check(
            "google_credentials",
            "error",
            f"Google OAuth credentials missing or unreadable: {exc}",
            fix="Place client credentials at ~/.g_gremlin/credentials.json or set G_GREMLIN_CREDENTIALS=/path/to/credentials.json.",
        )

    token_path = google_mod.TOKEN_PATH
    if token_path.exists():
        try:
            token_data = json.loads(token_path.read_text(encoding="utf-8"))
            scopes = token_data.get("scopes") or []
            expiry_dt = _parse_iso8601(token_data.get("expiry"))
            missing_scopes = [s for s in google_mod.SCOPES if s not in scopes]
            status = "ok"
            detail_parts = [f"token.json present at {token_path}"]
            if expiry_dt:
                remaining = expiry_dt - datetime.now(timezone.utc)
                if remaining.total_seconds() < 0:
                    status = "error"
                    detail_parts.append(f"expired {abs(int(remaining.total_seconds() // 3600))}h ago")
                else:
                    detail_parts.append(f"expires in {int(remaining.total_seconds() // 3600)}h")
            if missing_scopes:
                status = "error"
                detail_parts.append(f"missing scopes: {', '.join(missing_scopes)}")
            add_check(
                "google_token",
                status,
                "; ".join(detail_parts),
                fix="Re-auth: rm ~/.g_gremlin/token.json && run g-gremlin pull SHEET_ID --range A1:B1 to refresh token.",
                meta={"token_path": str(token_path), "missing_scopes": missing_scopes},
            )
        except Exception as exc:
            add_check(
                "google_token",
                "error",
                f"Failed to read token.json: {exc}",
                fix="Delete the corrupted token and re-auth: rm ~/.g_gremlin/token.json && run g-gremlin pull SHEET_ID --range A1:B1.",
            )
    else:
        add_check(
            "google_token",
            "error",
            f"No OAuth token found at {token_path}.",
            fix="Run: g-gremlin pull SHEET_ID --range A1:B1 (opens browser for OAuth).",
        )

    # Optional live Sheets ping
    if live_ping:
        if not spreadsheet_id:
            add_check(
                "sheets_ping",
                "error",
                "--live requires SPREADSHEET_ID.",
                fix="Re-run: g-gremlin doctor SHEET_ID --live",
            )
        else:
            try:
                service = google_mod.get_sheets_service()
                meta = (
                    service.spreadsheets()
                    .get(spreadsheetId=spreadsheet_id, fields="spreadsheetId,properties.title")
                    .execute()
                )
                title = meta.get("properties", {}).get("title", "")
                add_check(
                    "sheets_ping",
                    "ok",
                    f"Sheets API reachable for {spreadsheet_id} ({title or 'no title'})",
                    meta={"spreadsheet_id": spreadsheet_id, "title": title},
                )
            except Exception as exc:
                add_check(
                    "sheets_ping",
                    "error",
                    f"Sheets API ping failed: {exc}",
                    fix="Refresh auth (rm ~/.g_gremlin/token.json) or verify the sheet ID and network connectivity.",
                )

    # Optional SFDC check
    if check_sfdc:
        try:
            sf_cmd = cli_mod._get_sfdc_helpers()._find_sf_cli()
        except Exception as exc:
            add_check(
                "sfdc_cli",
                "error",
                f"Salesforce CLI missing: {exc}",
                fix="Install Salesforce CLI: https://developer.salesforce.com/tools/sfdxcli and ensure sf is on PATH.",
            )
            sf_cmd = None

        if sf_cmd:
            alias = cli_mod._get_sfdc_helpers().get_configured_org()
            cmd = [sf_cmd, "org", "display", "--json"]
            if alias:
                cmd.extend(["--target-org", alias])
            proc = subprocess.run(cmd, capture_output=True, text=True)
            if proc.returncode == 0:
                username = None
                org_alias = alias
                try:
                    parsed = json.loads(proc.stdout or "{}")
                    username = parsed.get("result", {}).get("username")
                    org_alias = org_alias or parsed.get("result", {}).get("alias")
                except Exception:
                    pass
                add_check(
                    "sfdc_auth",
                    "ok",
                    f"Salesforce CLI authenticated (alias={org_alias or 'default'}, user={username or 'n/a'})",
                    meta={"org_alias": org_alias, "username": username},
                )
            else:
                message = proc.stderr.strip() or proc.stdout.strip() or "SF CLI returned non-zero."
                add_check(
                    "sfdc_auth",
                    "error",
                    f"Salesforce CLI auth check failed: {message}",
                    fix="Log in: sf org login web --alias <alias> && g-gremlin sfdc connect --target <alias>.",
                )

    # Credential provider status check
    try:
        provider = cli_mod._get_credential_provider()
        statuses = provider.list_integrations()

        configured = [s for s in statuses if s.configured]
        plaintext = [s for s in statuses if s.configured and s.is_plaintext]

        if configured:
            details = ", ".join(f"{s.integration}({s.source.value})" for s in configured[:5])
            if len(configured) > 5:
                details += f" +{len(configured) - 5} more"
            add_check(
                "credentials",
                "ok",
                f"{len(configured)} integrations configured: {details}",
                meta={"configured": [s.integration for s in configured]},
            )
        else:
            add_check(
                "credentials",
                "warn",
                "No integrations configured. Run g-gremlin auth status for details.",
                fix="Configure with: g-gremlin auth set <integration>",
            )

        # Check for legacy plaintext files
        if plaintext:
            add_check(
                "credentials_plaintext",
                "warn",
                f"{len(plaintext)} secret(s) stored in plaintext files: {', '.join(s.integration for s in plaintext)}",
                fix="Migrate to secure storage: g-gremlin auth migrate",
            )

        # Check keychain availability
        if not provider.has_keychain_backend():
            add_check(
                "keychain",
                "warn",
                "No keychain backend available (macOS Keychain or Windows Credential Manager).",
                fix="Keyring ships with g-gremlin but no OS credential store was detected.",
            )
    except Exception as exc:
        add_check(
            "credentials",
            "error",
            f"Failed to check credentials: {exc}",
        )

    # Build summary + exit code
    errors = sum(1 for c in checks if c["status"] == "error")
    warnings = sum(1 for c in checks if c["status"] == "warn")
    python_ok = sys.version_info >= (3, 11)
    python_summary: dict[str, Any] = {"ok": python_ok, "version": python_version}
    if not python_ok:
        python_summary["error"] = "Python 3.11+ required"

    auth_summary: dict[str, Any] = {"ok": bool(license_result and license_result.valid)}
    if license_result and license_result.license:
        lic = license_result.license
        from g_gremlin.credentials.license import _is_trial_tier
        auth_summary["type"] = (
            "trial" if _is_trial_tier(lic.tier) else "paid" if lic.tier == "agent_paid" else lic.tier
        )
        auth_summary["status"] = lic.status
        if lic.days_remaining is not None:
            auth_summary["days_remaining"] = lic.days_remaining
        if lic.trial_ends_at:
            auth_summary["trial_ends_at"] = lic.trial_ends_at
    if not auth_summary["ok"]:
        auth_summary["error"] = license_result.error if license_result else "License check unavailable"

    if not check_sfdc:
        salesforce_summary = {
            "ok": None,
            "skipped": True,
            "reason": "Run with --sfdc to check Salesforce CLI/auth.",
        }
    else:
        sfdc_cli = next((c for c in checks if c["name"] == "sfdc_cli"), None)
        sfdc_auth = next((c for c in checks if c["name"] == "sfdc_auth"), None)
        if sfdc_cli and sfdc_cli["status"] == "error":
            salesforce_summary = {"ok": False, "error": sfdc_cli["detail"]}
        elif sfdc_auth and sfdc_auth["status"] == "error":
            salesforce_summary = {"ok": False, "error": sfdc_auth["detail"]}
        elif sfdc_auth and sfdc_auth["status"] == "ok":
            meta = sfdc_auth.get("meta") or {}
            salesforce_summary = {"ok": True, **{k: v for k, v in meta.items() if v}}
        else:
            salesforce_summary = {"ok": False, "error": "Salesforce check failed or incomplete."}

    report = {
        "status": "ok" if errors == 0 else "issues_found",
        "errors": errors,
        "warnings": warnings,
        "version": __version__,
        "checks": checks,
        "python": python_summary,
        "auth": auth_summary,
        "salesforce": salesforce_summary,
    }

    if output_format.lower() == "json":
        payload = json.dumps(report, indent=2)
        _emit_json_payload(payload, output_path)
    else:
        lines = [f"g-gremlin doctor (v{__version__})"]
        for check in checks:
            label = check["status"].upper()
            lines.append(f"- {check['name']}: {label} - {check['detail']}")
            if check.get("fix") and check["status"] != "ok":
                lines.append(f"  Fix: {check['fix']}")
        lines.append(f"Result: {'OK' if errors == 0 else 'ISSUES FOUND'} (errors={errors}, warnings={warnings})")
        output_text = "\n".join(lines)
        if output_path and str(output_path) != "-":
            output_path.parent.mkdir(parents=True, exist_ok=True)
            output_path.write_text(output_text, encoding="utf-8")
        else:
            click.echo(output_text)

    if errors:
        sys.exit(EXIT_USAGE)
