from __future__ import annotations

import argparse
import json
import os
import sys
from pathlib import Path

from mikoshi.config import ConfigError, configure_external_libs, load_config
from mikoshi.auth import (
    AuthError,
    DEFAULT_API_BASE_URL,
    clear_auth_state,
    email_from_token,
    is_expired,
    load_auth_state,
    login,
    save_broker_config,
)
from mikoshi.indexing.index_store import IndexStore
from mikoshi.utils.types import SearchResult


def _format_index_root(path: Path) -> str:
    try:
        home = Path.home().resolve()
        resolved = path.expanduser().resolve()
        if resolved == home:
            return "~"
        if str(resolved).startswith(str(home) + "/"):
            return str(resolved).replace(str(home), "~", 1)
        return str(resolved)
    except Exception:
        return str(path)


def _search(repo_path: str, query: str, k: int) -> list[SearchResult]:
    from mikoshi.retrieval.hybrid import search_repo

    return search_repo(repo_path, query, k)


def cmd_index(args: argparse.Namespace) -> int:
    try:
        from mikoshi.indexing.indexer import index_repo

        result = index_repo(args.path)
    except ConfigError as exc:
        print(f"Config error: {exc}", file=sys.stderr)
        return 2
    print(
        json.dumps(
            {
                "repo_id": result.repo_id,
                "chunks_indexed": result.chunks_indexed,
                "took_ms": result.took_ms,
            },
            indent=2,
        )
    )
    return 0


def cmd_search(args: argparse.Namespace) -> int:
    try:
        results = _search(args.path, args.query, args.k)
    except ConfigError as exc:
        print(f"Config error: {exc}", file=sys.stderr)
        return 2
    except RuntimeError as exc:
        print(str(exc), file=sys.stderr)
        return 1

    for result in results:
        print(f"{result.relpath}:{result.start_line}-{result.end_line} ({result.score:.3f})")
        print(result.snippet)
        print()
    return 0


def cmd_doctor(args: argparse.Namespace) -> int:
    exit_code = 0
    major, minor = sys.version_info[:2]
    version_label = f"{major}.{minor}.x"
    if (major, minor) >= (3, 11):
        print(f"✅ Python: {version_label}")
    else:
        print(f"❌ Python: {version_label} (requires 3.11+)")
        exit_code = 1

    try:
        config = load_config()
    except ConfigError as exc:
        print(f"❌ Config: {exc}")
        return 1

    print(f"✅ Mikoshi index root: {_format_index_root(config.index_root)}")
    print(
        f"✅ Embeddings: provider={config.embeddings.provider} "
        f"model={config.embeddings.model}"
    )

    model_cached = True
    if config.embeddings.provider == "local":
        try:
            from huggingface_hub import snapshot_download

            try:
                snapshot_download(
                    config.embeddings.model,
                    local_files_only=True,
                )
                model_cached = True
            except Exception:
                model_cached = False
        except Exception:
            print("❌ Dependencies: huggingface_hub missing")
            return 1

    offline = os.getenv("MIKOSHI_OFFLINE", "").strip().lower() in {
        "1",
        "true",
        "yes",
        "on",
    }
    if config.embeddings.provider == "local":
        if not model_cached and offline:
            print("❌ Model cached: no (offline)")
            exit_code = 1
        else:
            print(f"✅ Model cached: {'yes' if model_cached else 'no'}")
    else:
        print("✅ Model cached: yes")

    if args.path:
        repo_root = Path(args.path).expanduser().resolve()
        store = IndexStore(repo_root, config.index_root)
        meta = store.load_meta()
        if meta:
            print(
                "✅ Repo indexed: yes "
                f"(chunks={meta.chunks}, last_index_time={meta.updated_at})"
            )
        else:
            print("✅ Repo indexed: no")

    return exit_code


def cmd_login(args: argparse.Namespace) -> int:
    state = load_auth_state()
    if state and not is_expired(state):
        print("⚠️ You are already logged in to Mikoshi.")
        print("Re-authenticating will replace your current session.")
        answer = input(
            "Do you want to continue with re-authentication? "
            "This will invalidate your existing session if successful. (y/N): "
        ).strip().lower()
        if answer not in {"y", "yes"}:
            return 0
    try:
        email = login()
    except AuthError as exc:
        print(str(exc), file=sys.stderr)
        return 2
    print(f"✅ Logged in as {email}")
    return 0


def cmd_logout(args: argparse.Namespace) -> int:
    clear_auth_state()
    print("✅ Logged out")
    return 0


def cmd_whoami(args: argparse.Namespace) -> int:
    state = load_auth_state()
    if not state or is_expired(state):
        print("🔒 Not signed in")
        return 1
    email = email_from_token(state.access_token) or "unknown"
    plan = state.plan.title() if state.plan else "Free"
    print(f"✅ {email} ({plan})")
    return 0


def cmd_auth_configure(args: argparse.Namespace) -> int:
    prompt = f"API base URL [{DEFAULT_API_BASE_URL}]: "
    api_base_url = input(prompt).strip() or DEFAULT_API_BASE_URL
    try:
        save_broker_config(api_base_url)
    except AuthError as exc:
        print(str(exc), file=sys.stderr)
        return 2
    print("✅ Auth configured")
    return 0


def cmd_status(args: argparse.Namespace) -> int:
    config = load_config()
    repo_root = Path(args.path).expanduser().resolve()
    store = IndexStore(repo_root, config.index_root)
    meta = store.load_meta()
    if not meta:
        print(
            json.dumps(
                {
                    "indexed": False,
                    "chunks": 0,
                    "last_index_time": None,
                    "model": None,
                },
                indent=2,
            )
        )
        return 0
    print(
        json.dumps(
            {
                "indexed": True,
                "chunks": meta.chunks,
                "last_index_time": meta.updated_at,
                "model": meta.model,
            },
            indent=2,
        )
    )
    return 0


def cmd_clear(args: argparse.Namespace) -> int:
    config = load_config()
    repo_root = Path(args.path).expanduser().resolve()
    store = IndexStore(repo_root, config.index_root)
    store.clear()
    print(json.dumps({"ok": True}))
    return 0


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="mikoshi")
    parser.add_argument(
        "--verbose",
        action="store_true",
        help="Enable verbose external library output",
    )
    sub = parser.add_subparsers(dest="command", required=True)

    index_parser = sub.add_parser("index", help="Index a repository")
    index_parser.add_argument("path", help="Path to repository")
    index_parser.set_defaults(func=cmd_index)

    search_parser = sub.add_parser("search", help="Search an indexed repository")
    search_parser.add_argument("path", help="Path to repository")
    search_parser.add_argument("query", help="Search query")
    search_parser.add_argument("--k", type=int, default=8, help="Number of results")
    search_parser.set_defaults(func=cmd_search)

    doctor_parser = sub.add_parser("doctor", help="Check Mikoshi setup")
    doctor_parser.add_argument("path", nargs="?", help="Optional repo path")
    doctor_parser.set_defaults(func=cmd_doctor)

    status_parser = sub.add_parser("status", help="Show index status")
    status_parser.add_argument("path", help="Path to repository")
    status_parser.set_defaults(func=cmd_status)

    clear_parser = sub.add_parser("clear", help="Clear index data")
    clear_parser.add_argument("path", help="Path to repository")
    clear_parser.set_defaults(func=cmd_clear)

    login_parser = sub.add_parser("login", help="Sign in")
    login_parser.set_defaults(func=cmd_login)

    logout_parser = sub.add_parser("logout", help="Clear local auth state")
    logout_parser.set_defaults(func=cmd_logout)

    whoami_parser = sub.add_parser("whoami", help="Show current auth status")
    whoami_parser.set_defaults(func=cmd_whoami)

    auth_parser = sub.add_parser("auth", help="Auth configuration")
    auth_sub = auth_parser.add_subparsers(dest="auth_command", required=True)
    auth_configure = auth_sub.add_parser("configure", help="Set auth config")
    auth_configure.set_defaults(func=cmd_auth_configure)

    return parser


def main(argv: list[str] | None = None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)
    quiet = not args.verbose
    os.environ["MIKOSHI_QUIET_EXTERNAL_LIBS"] = "1" if quiet else "0"
    configure_external_libs(quiet)
    return int(args.func(args))


if __name__ == "__main__":
    raise SystemExit(main())
