"""Giriş noktası: argüman ayrıştırma, kurulum sihirbazı, REPL."""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

import openai
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.formatted_text import HTML
from prompt_toolkit.history import FileHistory
from openai import OpenAI

from . import __version__
from . import tools as T
from . import ui
from .agent import Agent
from .config import BASE_URL_CANDIDATES, ENV_PATH, PROJECT_ROOT, Config

HISTORY_FILE = PROJECT_ROOT / ".qwen_history"

SLASH = [
    "/help", "/model", "/models", "/clear", "/history", "/cd", "/pwd",
    "/yolo", "/tools", "/undo", "/save", "/setup", "/exit", "/quit",
]


# --------------------------------------------------------------------- sihirbaz

def probe(base_url: str, api_key: str, timeout: float = 15.0) -> list[str]:
    """Endpoint'e bağlanıp model listesini döndürür. Hata olursa fırlatır."""
    client = OpenAI(api_key=api_key or "dummy", base_url=base_url.rstrip("/"),
                    timeout=timeout, max_retries=0)
    return [m.id for m in client.models.list().data]


def run_setup(cfg: Config) -> Config:
    ui.console.print()
    ui.console.rule("[bold cyan]qwen-cli kurulumu[/bold cyan]")
    ui.info(
        "API anahtarını experiments.hetzner.com/docs/inference adresinden alabilirsin.\n"
        "Base URL'yi de aynı sayfadaki örnekten kopyala (OpenAI uyumlu, /v1 ile biten adres)."
    )
    ui.console.print()

    key = ui.console.input(
        f"[bold]API anahtarı[/bold] [grey58](mevcut: {cfg.masked_key()}, boş = değiştirme)[/grey58]: "
    ).strip()
    if key:
        cfg.api_key = key
    if not cfg.api_key:
        ui.error("API anahtarı olmadan devam edilemez.")
        sys.exit(1)

    entered = ui.console.input(
        f"[bold]Base URL[/bold] [grey58](boş = otomatik dene, mevcut: {cfg.base_url})[/grey58]: "
    ).strip()

    candidates = [entered] if entered else [cfg.base_url] + [
        c for c in BASE_URL_CANDIDATES if c != cfg.base_url
    ]

    models: list[str] = []
    for url in candidates:
        url = url.rstrip("/")
        ui.console.print(f"  [grey58]deneniyor:[/grey58] {url} ... ", end="")
        try:
            models = probe(url, cfg.api_key)
        except openai.AuthenticationError:
            ui.console.print("[red]anahtar reddedildi[/red]")
            ui.error("API anahtarı geçersiz görünüyor. Anahtarı kontrol edip tekrar dene.")
            sys.exit(1)
        except Exception as exc:  # noqa: BLE001
            ui.console.print(f"[red]olmadı[/red] [grey58]({type(exc).__name__})[/grey58]")
            continue
        ui.console.print(f"[green]bağlandı[/green] [grey58]({len(models)} model)[/grey58]")
        cfg.base_url = url
        break

    if not models:
        ui.console.print()
        ui.error(
            "Hiçbir base URL tutmadı. Doğru adresi dokümandan alıp .env içine\n"
            "  QWEN_BASE_URL=https://.../v1\n"
            "şeklinde yaz, sonra tekrar dene."
        )
        cfg.save()
        sys.exit(1)

    ui.console.print()
    ui.console.print("[bold]Kullanılabilir modeller:[/bold]")
    for i, m in enumerate(models, 1):
        mark = " [green](varsayılan)[/green]" if m == cfg.model else ""
        ui.console.print(f"  [cyan]{i:>2}[/cyan]  {m}{mark}")

    choice = ui.console.input(
        "\n[bold]Model[/bold] [grey58](numara veya tam ad, boş = ilk sıradaki)[/grey58]: "
    ).strip()
    if not choice:
        cfg.model = models[0]
    elif choice.isdigit() and 1 <= int(choice) <= len(models):
        cfg.model = models[int(choice) - 1]
    else:
        cfg.model = choice

    cfg.save()
    ui.console.print()
    ui.ok(f"Ayarlar {ENV_PATH} dosyasına yazıldı. Model: [bold]{cfg.model}[/bold]")
    ui.console.print()
    return cfg


# --------------------------------------------------------------------- komutlar

def handle_slash(line: str, agent: Agent) -> bool:
    """Slash komutunu işler. False dönerse REPL'den çıkılır."""
    parts = line.strip().split(maxsplit=1)
    cmd = parts[0].lower()
    arg = parts[1].strip() if len(parts) > 1 else ""
    cfg = agent.cfg

    if cmd in ("/exit", "/quit", "/q"):
        return False

    if cmd == "/help":
        ui.help_table()

    elif cmd == "/tools":
        ui.tools_table(T.SCHEMAS)

    elif cmd == "/model":
        if arg:
            cfg.model = arg
            cfg.save()
            ui.ok(f"model: {arg}")
        else:
            ui.info(f"model: {cfg.model}")

    elif cmd == "/models":
        try:
            models = agent.list_models()
        except Exception as exc:  # noqa: BLE001
            ui.error(f"Model listesi alınamadı: {exc}")
        else:
            for m in models:
                mark = "  [green]<- aktif[/green]" if m == cfg.model else ""
                ui.console.print(f"  {m}{mark}")

    elif cmd == "/clear":
        agent.clear()
        ui.ok("geçmiş temizlendi")

    elif cmd == "/history":
        chars = sum(len(str(m.get("content") or "")) for m in agent.messages)
        ui.info(
            f"{len(agent.messages)} mesaj, ~{chars // 4:,} token (kaba tahmin) · "
            f"oturum: {agent.total_in:,} girdi / {agent.total_out:,} çıktı"
        )

    elif cmd == "/pwd":
        ui.info(str(agent.ws.root))

    elif cmd == "/cd":
        if not arg:
            ui.error("kullanım: /cd <dizin>")
        else:
            p = Path(arg).expanduser()
            p = p if p.is_absolute() else agent.ws.root / p
            if p.is_dir():
                agent.set_workspace(p.resolve())
                ui.ok(f"çalışma dizini: {agent.ws.root}")
            else:
                ui.error(f"dizin yok: {p}")

    elif cmd == "/yolo":
        cfg.yolo = not cfg.yolo
        (ui.warn if cfg.yolo else ui.ok)(
            "YOLO açık - hiçbir işlem için onay sorulmayacak." if cfg.yolo
            else "YOLO kapalı - değişiklikler tekrar onaya tabi."
        )

    elif cmd == "/undo":
        ui.info(agent.undo())

    elif cmd == "/save":
        if not arg:
            ui.error("kullanım: /save <dosya>")
        elif not agent.last_reply:
            ui.error("kaydedilecek yanıt yok")
        else:
            target = Path(arg)
            target = target if target.is_absolute() else agent.ws.root / target
            target.parent.mkdir(parents=True, exist_ok=True)
            target.write_text(agent.last_reply, encoding="utf-8")
            ui.ok(f"{target} yazıldı")

    elif cmd == "/setup":
        run_setup(cfg)
        agent.__init__(cfg)  # yeni ayarlarla istemciyi tazele

    else:
        ui.error(f"bilinmeyen komut: {cmd}  (/help)")

    return True


# --------------------------------------------------------------------- REPL

def make_session() -> PromptSession | None:
    """Satır düzenlemeli girdi oturumu. Windows konsolu yoksa (Git Bash/MinTTY,
    boru hattı) None döner ve düz input() kullanılır."""
    try:
        return PromptSession(
            history=FileHistory(str(HISTORY_FILE)),
            completer=WordCompleter(SLASH, sentence=True),
            complete_while_typing=False,
        )
    except Exception:  # noqa: BLE001 - NoConsoleScreenBufferError vb.
        return None


def read_input(session: PromptSession | None) -> str:
    """Tek satır okur; satır '\\' ile bitiyorsa devam satırı ister."""
    buf: list[str] = []
    while True:
        if session is not None:
            prompt = HTML("<ansicyan><b>you</b></ansicyan><ansibrightblack> ></ansibrightblack> ") \
                if not buf else HTML("<ansibrightblack>  ...</ansibrightblack> ")
            line = session.prompt(prompt)
        else:
            ui.console.print(
                "[bold cyan]you[/bold cyan][grey58] >[/grey58] " if not buf
                else "[grey58]  ...[/grey58] ", end="")
            line = input()
        if line.endswith("\\"):
            buf.append(line[:-1])
            continue
        buf.append(line)
        return "\n".join(buf)


def repl(agent: Agent) -> None:
    session = make_session()
    if session is None:
        ui.info("(basit girdi modu: geçmiş/tamamlama yok — cmd.exe veya Windows "
                "Terminal'de tam özellikli çalışır)")
    while True:
        try:
            line = read_input(session)
        except KeyboardInterrupt:
            continue
        except EOFError:
            ui.console.print("[grey58]görüşürüz.[/grey58]")
            return

        text = line.strip()
        if not text:
            continue
        if text.startswith("/"):
            if not handle_slash(text, agent):
                ui.console.print("[grey58]görüşürüz.[/grey58]")
                return
            continue

        try:
            agent.run(text)
        except KeyboardInterrupt:
            ui.warn("iptal edildi")
        except Exception as exc:  # noqa: BLE001
            ui.error(f"{type(exc).__name__}: {exc}")


# --------------------------------------------------------------------- main

def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(
        prog="qwen",
        description="Hetzner Inference (Qwen) üzerinde çalışan terminal kodlama ajanı.",
    )
    p.add_argument("prompt", nargs="*", help="tek seferlik istek (verilmezse REPL açılır)")
    p.add_argument("-p", "--print", dest="oneshot", metavar="ISTEK",
                   help="tek istek çalıştır ve çık")
    p.add_argument("-m", "--model", help="model adı")
    p.add_argument("-C", "--workspace", metavar="DIZIN", help="çalışma dizini (varsayılan: cwd)")
    p.add_argument("--base-url", help="OpenAI uyumlu endpoint")
    p.add_argument("--tool-mode", choices=["auto", "native", "prompt"],
                   help="araç çağırma biçimi")
    p.add_argument("--yolo", action="store_true", help="hiçbir işlem için onay sorma")
    p.add_argument("--unsafe", action="store_true",
                   help="çalışma dizini dışındaki yollara da izin ver")
    p.add_argument("--setup", action="store_true", help="kurulum sihirbazını çalıştır")
    p.add_argument("--version", action="version", version=f"qwen-cli {__version__}")
    return p


def main(argv: list[str] | None = None) -> int:
    for stream in (sys.stdout, sys.stderr):
        try:
            stream.reconfigure(encoding="utf-8")  # type: ignore[union-attr]
        except (AttributeError, ValueError):
            pass

    args = build_parser().parse_args(argv)
    cfg = Config.load()

    if args.model:
        cfg.model = args.model
    if args.base_url:
        cfg.base_url = args.base_url.rstrip("/")
    if args.tool_mode:
        cfg.tool_mode = args.tool_mode
    cfg.yolo = args.yolo
    cfg.unsafe = args.unsafe
    if args.workspace:
        ws = Path(args.workspace).expanduser().resolve()
        if not ws.is_dir():
            ui.error(f"dizin yok: {ws}")
            return 1
        cfg.workspace = ws

    if args.setup or not cfg.is_configured:
        if not cfg.is_configured and not args.setup:
            ui.warn("API anahtarı bulunamadı, kurulum başlatılıyor.")
        cfg = run_setup(cfg)
        if args.workspace:
            cfg.workspace = Path(args.workspace).expanduser().resolve()
        cfg.yolo = args.yolo
        cfg.unsafe = args.unsafe

    agent = Agent(cfg)

    oneshot = args.oneshot or (" ".join(args.prompt).strip() if args.prompt else "")
    if oneshot:
        try:
            agent.run(oneshot)
        except KeyboardInterrupt:
            ui.warn("iptal edildi")
            return 130
        return 0

    ui.banner(cfg.model, cfg.base_url, str(agent.ws.root), agent.tool_mode, cfg.yolo)
    repl(agent)
    return 0


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