from __future__ import annotations

import json
import sys

import typer
from loguru import logger

from flow_xhs.cli.spec import (
    FLOW_XHS_SPEC,
    export_command_spec,
    export_group_view,
    export_root_help,
    get_command_spec,
    get_group_view,
    global_option,
    render_command_help,
    render_group_help,
    render_root_help,
)
from flow_xhs.cli.spec.global_options import build_global_option_registry
from flow_xhs.runtime.config import is_browser_headful_forced
from flow_xhs.runtime.database.manager import init_db
from flow_xhs import __version__
from flow_xhs.platform import REDNOTE_PLATFORM
from flow_xhs.platform.registration import register_flow_xhs_runtime
from flow_xhs.cli.commands import account, doctor, init, note, user
from flow_xhs.cli.context import CliContext
from flow_xhs.runtime.logging import setup_cli_logger
from flow_xhs.runtime.paths import configure_legacy_data_paths, ensure_runtime_dirs

APP_HELP = render_root_help(FLOW_XHS_SPEC)

app = typer.Typer(
    name=FLOW_XHS_SPEC.cli_name,
    help=APP_HELP,
    add_completion=False,
    no_args_is_help=True,
    options_metavar="[OPTIONS]",
    subcommand_metavar="<COMMAND>",
)

_GLOBAL_OPTION_REGISTRY = build_global_option_registry(FLOW_XHS_SPEC)
HELP_FLAGS = _GLOBAL_OPTION_REGISTRY.help_flags
GLOBAL_VALUE_OPTIONS = _GLOBAL_OPTION_REGISTRY.value_options
GLOBAL_FLAG_OPTIONS = _GLOBAL_OPTION_REGISTRY.flag_options

app.add_typer(init.app, name="init")
app.add_typer(doctor.app, name="doctor")
app.add_typer(account.app, name="account")
app.add_typer(note.app, name="note")
app.add_typer(user.app, name="user")


def _version_callback(value: bool) -> None:
    if not value:
        return

    typer.echo(__version__)
    raise typer.Exit(code=0)


@app.callback()
def main(
    ctx: typer.Context,
    format: str = global_option("format"),
    out: str | None = global_option("out"),
    quiet: bool = global_option("quiet", False),
    version: bool = global_option("version", False, callback=_version_callback, is_eager=True),
    trace_id: str | None = global_option("trace_id"),
):
    if format not in {"json", "jsonl", "table"}:
        raise typer.BadParameter("--format 仅支持 json|jsonl|table")

    register_flow_xhs_runtime()
    ensure_runtime_dirs()
    configure_legacy_data_paths()
    init_db()
    setup_cli_logger(quiet=quiet)
    if is_browser_headful_forced():
        logger.warning("Browser headful mode forced by env (FLOW_XHS_HEADFUL/BROWSER_HEADFUL).")

    ctx.obj = CliContext(
        platform_name=REDNOTE_PLATFORM,
        output_format=format,
        out_file=out,
        quiet=quiet,
        trace_id=trace_id,
    )


def _skip_option_value(args: list[str], index: int) -> int:
    token = args[index]
    if token in GLOBAL_VALUE_OPTIONS and "=" not in token and index + 1 < len(args):
        return index + 2
    return index + 1


def _extract_help_path(args: list[str]) -> tuple[tuple[str, ...], bool, bool]:
    path: list[str] = []
    started_path = False
    i = 0
    while i < len(args):
        token = args[i]
        if token in HELP_FLAGS:
            return tuple(path), True, (i + 1) < len(args)
        if token.startswith("-"):
            if started_path:
                break
            if token in GLOBAL_FLAG_OPTIONS:
                i += 1
                continue
            if token in GLOBAL_VALUE_OPTIONS:
                i = _skip_option_value(args, i)
                continue
            if any(token.startswith(f"{option}=") for option in GLOBAL_VALUE_OPTIONS):
                i += 1
                continue
            return tuple(path), False, False
        started_path = True
        path.append(token)
        i += 1
    return tuple(path), False, False


def _extract_command_path(args: list[str]) -> tuple[tuple[str, ...], list[str]]:
    path: list[str] = []
    i = 0
    while i < len(args):
        token = args[i]
        if token.startswith("-"):
            i = _skip_option_value(args, i)
            continue
        break
    while i < len(args):
        token = args[i]
        if token.startswith("-"):
            break
        path.append(token)
        i += 1
    return tuple(path), args[i:]


def _extract_global_option_value(args: list[str], option_name: str) -> str | None:
    i = 0
    while i < len(args):
        token = args[i]
        if token == option_name:
            if i + 1 >= len(args):
                return None
            return args[i + 1]
        prefix = f"{option_name}="
        if token.startswith(prefix):
            return token[len(prefix):]
        i = _skip_option_value(args, i)
    return None


def _has_global_flag(args: list[str], option_name: str) -> bool:
    return option_name in args


def _contains_unknown_global_option(args: list[str]) -> bool:
    i = 0
    while i < len(args):
        token = args[i]
        if token == "--":
            break
        if not token.startswith("-"):
            break
        if token in HELP_FLAGS or token in GLOBAL_FLAG_OPTIONS:
            i += 1
            continue
        if token in GLOBAL_VALUE_OPTIONS:
            if i + 1 >= len(args) or args[i + 1] == "--":
                return True
            i += 2
            continue
        if any(token.startswith(f"{option}=") for option in GLOBAL_VALUE_OPTIONS):
            i += 1
            continue
        return True
    return False


def _render_spec_output(*, text: str, payload: dict[str, object] | None, args: list[str]) -> None:
    output_format = _extract_global_option_value(args, "--format") or "table"
    if output_format in {"json", "jsonl"}:
        typer.echo(json.dumps(payload or {"kind": "text", "text": text}, ensure_ascii=False))
        return
    typer.echo(text)


def _try_render_spec_help() -> bool:
    args = sys.argv[1:]
    if not args:
        _render_spec_output(
            text=render_root_help(FLOW_XHS_SPEC),
            payload=export_root_help(FLOW_XHS_SPEC),
            args=args,
        )
        return True

    path, saw_help, has_trailing_after_help = _extract_help_path(args)
    if saw_help:
        if has_trailing_after_help:
            return False
        if not path:
            _render_spec_output(
                text=render_root_help(FLOW_XHS_SPEC),
                payload=export_root_help(FLOW_XHS_SPEC),
                args=args,
            )
            return True
        command = get_command_spec(FLOW_XHS_SPEC, path)
        if command is not None:
            _render_spec_output(
                text=render_command_help(FLOW_XHS_SPEC, command),
                payload=export_command_spec(command),
                args=args,
            )
            return True
        group = get_group_view(FLOW_XHS_SPEC, path)
        if group is not None:
            _render_spec_output(
                text=render_group_help(group),
                payload=export_group_view(group),
                args=args,
            )
            return True
        return False

    if _has_global_flag(args, "--version"):
        return False

    path, trailing = _extract_command_path(args)
    if not path and not trailing:
        if _contains_unknown_global_option(args):
            return False
        _render_spec_output(
            text=render_root_help(FLOW_XHS_SPEC),
            payload=export_root_help(FLOW_XHS_SPEC),
            args=args,
        )
        return True
    if path and not trailing:
        if get_command_spec(FLOW_XHS_SPEC, path) is not None:
            return False
        group = get_group_view(FLOW_XHS_SPEC, path)
        if group is not None:
            _render_spec_output(
                text=render_group_help(group),
                payload=export_group_view(group),
                args=args,
            )
            return True

    return False


def run() -> None:
    if _try_render_spec_help():
        return
    app()


if __name__ == "__main__":
    run()
