"""Sondeo CLI — profile Python scripts and generate reports."""

from __future__ import annotations

import argparse
import importlib.util
import json
import sys
from io import StringIO
from pathlib import Path
from typing import Any, Literal, cast


def main(argv: list[str] | None = None) -> None:
    """Entry point for the sondeo CLI."""
    parser = argparse.ArgumentParser(
        prog="sondeo",
        description="Sondeo - Python code profiler and log reporter",
    )
    parser.add_argument(
        "--version",
        action="store_true",
        help="Show version and exit",
    )

    sub = parser.add_subparsers(dest="command")

    run_parser = sub.add_parser("run", help="Profile a Python script")
    run_parser.add_argument("script", help="Python script to profile")
    run_parser.add_argument(
        "--sort",
        default="cumulative",
        help="Sort key for profile stats (default: cumulative)",
    )
    run_parser.add_argument(
        "--top",
        type=int,
        default=20,
        help="Number of top cProfile entries to show (default: 20)",
    )
    run_parser.add_argument(
        "--report",
        dest="report_format",
        choices=["text", "json", "markdown", "ui"],
        default="text",
        help="Report output mode (default: text)",
    )
    # Backward-compatible alias.
    run_parser.add_argument(
        "--format",
        dest="report_format",
        choices=["text", "json", "markdown"],
        help=argparse.SUPPRESS,
    )
    run_parser.add_argument(
        "--output",
        help="Write report output to a file",
    )
    run_parser.add_argument(
        "--host",
        default="127.0.0.1",
        help="UI host (for --report ui, default: 127.0.0.1)",
    )
    run_parser.add_argument(
        "--port",
        type=int,
        default=8000,
        help="UI port (for --report ui, default: 8000)",
    )
    run_parser.add_argument(
        "--no-browser",
        action="store_true",
        help="Do not open a browser automatically in UI mode",
    )

    report_parser = sub.add_parser("report", help="Render a report from saved Sondeo JSON data")
    report_parser.add_argument("input", help="Path to a Sondeo JSON report file")
    report_parser.add_argument(
        "--format",
        choices=["text", "json", "markdown", "ui"],
        default="markdown",
        help="Output format (default: markdown)",
    )
    report_parser.add_argument(
        "--output",
        help="Write rendered output to a file",
    )
    report_parser.add_argument(
        "--host",
        default="127.0.0.1",
        help="UI host (for --format ui, default: 127.0.0.1)",
    )
    report_parser.add_argument(
        "--port",
        type=int,
        default=8000,
        help="UI port (for --format ui, default: 8000)",
    )
    report_parser.add_argument(
        "--no-browser",
        action="store_true",
        help="Do not open browser automatically in UI mode",
    )

    args = parser.parse_args(argv)

    if args.version:
        version_file = Path(__file__).parent.parent.parent / "VERSION"
        if version_file.exists():
            print(f"sondeo {version_file.read_text().strip()}")
        else:
            print("sondeo (unknown version)")
        return

    if args.command == "run":
        _run_script(args)
        return

    if args.command == "report":
        _render_saved_report(args)
        return

    parser.print_help()


def _write_text(path: str, content: str) -> None:
    target = Path(path).expanduser().resolve()
    target.write_text(content, encoding="utf-8")
    print(f"Report written to {target}")


def _to_json_text(payload: Any) -> str:
    return json.dumps(payload, indent=2)


def _run_script(args: argparse.Namespace) -> None:
    """Profile and run a Python script."""
    script_path = Path(args.script).resolve()

    if not script_path.exists():
        print(f"Error: {args.script} not found")
        sys.exit(1)

    import cProfile
    import pstats
    import time

    from sondeo.profiler import logging as logging_mod
    from sondeo.profiler.timer import TimingResult, _results
    from sondeo.reporter.console import ReportFormat, report

    prof = cProfile.Profile()
    start = time.perf_counter()
    started_at = time.time()

    spec = importlib.util.spec_from_file_location("__main__", script_path)
    if spec is None or spec.loader is None:
        print(f"Error: cannot load {args.script}")
        sys.exit(1)

    module = importlib.util.module_from_spec(spec)
    sys.modules["__main__"] = module

    with logging_mod.correlation_scope() as cid:
        logging_mod.emit_event(
            event_type="profile",
            level="debug",
            module=__name__,
            message=f"{script_path.name} start",
            correlation_id=cid,
            data={"phase": "start", "script": str(script_path)},
        )

        try:
            prof.runcall(spec.loader.exec_module, module)
        except SystemExit:
            pass
        finally:
            elapsed = (time.perf_counter() - start) * 1000.0

            stats_stream = StringIO()
            stats = pstats.Stats(prof, stream=stats_stream)
            stats.sort_stats(args.sort)
            total_calls = int(getattr(stats, "total_calls", 0))

            timing = TimingResult(
                name=script_path.name,
                elapsed_ms=elapsed,
                calls=total_calls,
                started_at=started_at,
                ended_at=time.time(),
                correlation_id=cid,
            )
            _results.append(timing)

            logging_mod.emit_event(
                event_type="profile",
                level="info",
                module=__name__,
                message=f"{script_path.name} end",
                correlation_id=cid,
                data={
                    "phase": "end",
                    "script": str(script_path),
                    "elapsed_ms": round(elapsed, 3),
                    "calls": total_calls,
                },
            )

            report_format = cast(ReportFormat | Literal["ui"], args.report_format)

            if report_format == "ui":
                from sondeo.reporter.ui import serve_report

                payload = cast(
                    dict[str, Any],
                    report(format="json", clear=True, print_output=False),
                )
                if args.output:
                    _write_text(args.output, _to_json_text(payload))

                serve_report(
                    data=payload,
                    host=args.host,
                    port=args.port,
                    open_browser=not args.no_browser,
                )
                return

            rendered = report(
                format=report_format,
                clear=True,
                print_output=not bool(args.output),
            )

            if args.output:
                if report_format == "json":
                    _write_text(args.output, _to_json_text(rendered))
                else:
                    _write_text(args.output, str(rendered))

            if report_format == "text":
                stats.print_stats(args.top)
                print(stats_stream.getvalue())


def _render_saved_report(args: argparse.Namespace) -> None:
    from sondeo.reporter.console import ReportFormat, render_report
    from sondeo.reporter.ui import serve_report

    input_path = Path(args.input).expanduser().resolve()
    if not input_path.exists():
        print(f"Error: {input_path} not found")
        sys.exit(1)

    try:
        payload = json.loads(input_path.read_text(encoding="utf-8"))
    except json.JSONDecodeError as exc:
        print(f"Error: {input_path} is not valid JSON ({exc})")
        sys.exit(1)

    if not isinstance(payload, dict):
        print("Error: report input JSON must be an object")
        sys.exit(1)

    if args.format == "ui":
        if args.output:
            _write_text(args.output, _to_json_text(payload))
        serve_report(
            data=payload,
            host=args.host,
            port=args.port,
            open_browser=not args.no_browser,
        )
        return

    rendered = render_report(
        payload,
        format=cast(ReportFormat, args.format),
        color=True,
    )
    output_text = _to_json_text(rendered) if args.format == "json" else str(rendered)

    if args.output:
        _write_text(args.output, output_text)
    else:
        print(output_text)
