#!/usr/bin/env python3
"""scan.py — scan the universe and display current signal scores as a table.

This command performs NO trades; it is completely safe and can be run in
DRY_RUN mode even without an API key. This is the FIRST command a user
should try.

Flow:
  1. Load settings (mode can optionally be overridden with --mode).
  2. Create the exchange wrapper and scan the universe (liquidity/volume).
  3. For each symbol, fetch OHLCV, drop the forming last bar, generate a signal.
  4. Rank the signals by score and display them in a rich table.

Network/exchange errors are caught with a user-friendly message.
"""
from __future__ import annotations

import argparse
import sys
import pathlib

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))

from rich.console import Console
from rich.table import Table

from coinbot.config import load_settings
from coinbot.exchange import Exchange, ExchangeError
from coinbot.screener import screen_universe
from coinbot.strategy import Signal, evaluate, is_buy, rank_signals

console = Console()


def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        prog="scan.py",
        description="Brimoza evren tarayicisi — islem YAPMAZ, sadece sinyal skorlarini gosterir.",
    )
    parser.add_argument(
        "--mode",
        default=None,
        choices=["dry_run", "testnet", "live"],
        help="Calisma modunu config.yaml/env uzerine yaz (opsiyonel).",
    )
    parser.add_argument(
        "--top",
        type=int,
        default=15,
        help="Tabloda gosterilecek en yuksek skorlu sembol sayisi (varsayilan: 15).",
    )
    return parser.parse_args(argv)


def _fmt_price(price: float) -> str:
    """Format the price with a reasonable number of decimals based on its magnitude."""
    if price >= 100:
        return f"{price:,.2f}"
    if price >= 1:
        return f"{price:.4f}"
    return f"{price:.8f}".rstrip("0").rstrip(".")


def _build_table(signals: list[Signal], threshold: float, top: int) -> Table:
    table = Table(
        title=f"Brimoza Tarama Sonuclari (esik={threshold:.2f})",
        show_lines=False,
    )
    table.add_column("#", justify="right", style="dim")
    table.add_column("Sembol", style="cyan", no_wrap=True)
    table.add_column("Skor", justify="right")
    table.add_column("AL?", justify="center")
    table.add_column("Fiyat", justify="right", style="white")
    table.add_column("Sebepler", style="green")

    for rank, sig in enumerate(signals[:top], start=1):
        buy = sig.score >= threshold
        buy_cell = "[bold green]EVET[/]" if buy else "[dim]hayir[/]"
        # Color the score according to the threshold.
        if buy:
            score_cell = f"[bold green]{sig.score:.3f}[/]"
        elif sig.score >= threshold * 0.66:
            score_cell = f"[yellow]{sig.score:.3f}[/]"
        else:
            score_cell = f"[dim]{sig.score:.3f}[/]"
        # Show only the short names of the reason labels (before ": ").
        short_reasons = ", ".join(r.split(":")[0] for r in sig.reasons) or "-"
        table.add_row(
            str(rank),
            sig.symbol,
            score_cell,
            buy_cell,
            _fmt_price(sig.price),
            short_reasons,
        )
    return table


def main(argv: list[str] | None = None) -> int:
    args = _parse_args(argv)

    try:
        settings = load_settings(mode_override=args.mode)
    except (RuntimeError, ValueError) as exc:
        console.print(f"[bold red]Ayar hatasi:[/] {exc}")
        return 2

    console.print(
        f"[bold]Brimoza[/] tarama basliyor — "
        f"mod=[cyan]{settings.mode.value}[/], "
        f"zaman dilimi=[cyan]{settings.timeframe}[/], "
        f"quote=[cyan]{settings.quote_asset}[/]"
    )

    threshold = settings.strategy.score_threshold

    try:
        ex = Exchange(settings)
        with console.status("[bold]Evren taraniyor (likidite/hacim)..."):
            symbols = screen_universe(ex, settings)
    except ExchangeError as exc:
        console.print(
            f"[bold red]Borsa hatasi:[/] {exc}\n"
            "[yellow]Internet baglantinizi kontrol edin; Binance API gecici olarak "
            "erisilemez olabilir.[/]"
        )
        return 1
    except Exception as exc:  # noqa: BLE001 - user-friendly last resort
        console.print(
            f"[bold red]Beklenmeyen ag/hatasi:[/] {exc}\n"
            "[yellow]Internet baglantinizi kontrol edip tekrar deneyin.[/]"
        )
        return 1

    if not symbols:
        console.print(
            "[yellow]Hicbir sembol hacim esigini gecemedi. "
            "config.yaml icindeki universe.min_24h_quote_volume degerini dusurebilirsiniz.[/]"
        )
        return 0

    console.print(f"[dim]{len(symbols)} sembol analiz ediliyor...[/]")

    signals: list[Signal] = []
    errors = 0
    with console.status("[bold]Sinyaller hesaplaniyor...") as status:
        for i, sym in enumerate(symbols, start=1):
            status.update(f"[bold]Sinyaller hesaplaniyor... ({i}/{len(symbols)}) {sym}")
            try:
                df = ex.get_klines(sym)
            except ExchangeError as exc:
                errors += 1
                console.print(f"[dim red]{sym}: klines alinamadi ({exc})[/]")
                continue
            if df is None or len(df) < 2:
                continue
            closed = df.iloc[:-1]  # drop the forming last bar
            sig = evaluate(sym, closed, settings)
            if sig is not None:
                signals.append(sig)

    ranked = rank_signals(signals)
    passed = [s for s in ranked if is_buy(s, settings)]

    if not ranked:
        console.print(
            "[yellow]Hicbir sembol icin sinyal uretilemedi "
            "(yetersiz gecmis veri olabilir).[/]"
        )
        return 0

    console.print(_build_table(ranked, threshold, args.top))
    console.print(
        f"\n[bold]{len(passed)}[/] / {len(ranked)} sembol AL esigini "
        f"([cyan]{threshold:.2f}[/]) gecti."
    )
    if errors:
        console.print(f"[dim red]{errors} sembol veri hatasi nedeniyle atlandi.[/]")
    if settings.mode.value == "dry_run":
        console.print(
            "[dim]Not: DRY_RUN modundasiniz; bu komut yalnizca bilgilendirme amaclidir, "
            "hicbir emir gonderilmez.[/]"
        )
    return 0


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