"""Fetch, search, and browse commands."""

from __future__ import annotations

import click
from rich.table import Table

from .common import console, pass_cfg_db, _get_config
from ..config import Config
from ..db import Database


def register(main):
    """Register all fetch/browse commands with the main CLI group."""
    main.add_command(fetch)
    main.add_command(classify)
    main.add_command(list_drafts)
    main.add_command(search)
    main.add_command(show)
    main.add_command(annotate)
    main.add_command(authors)
    main.add_command(network)


# ── fetch ────────────────────────────────────────────────────────────────────


@click.command()
@click.option("--keywords", "-k", multiple=True, help="Extra keywords to search for")
@click.option("--since", "-s", help="Only fetch drafts newer than this date (YYYY-MM-DD)")
@click.option("--download-text/--no-download-text", default=True, help="Download full text of drafts")
@click.option("--classify/--no-classify", default=True, help="Pre-filter with local Ollama classifier (saves Claude tokens)")
@pass_cfg_db
def fetch(cfg, db, keywords: tuple[str, ...], since: str | None, download_text: bool, classify: bool):
    """Fetch AI/agent drafts from IETF Datatracker."""
    from ..fetcher import Fetcher

    fetcher = Fetcher(cfg)

    kw_list = list(cfg.search_keywords)
    if keywords:
        kw_list.extend(keywords)

    try:
        drafts = fetcher.search_drafts(keywords=kw_list, since=since)
        console.print(f"Found [bold]{len(drafts)}[/] drafts from Datatracker")

        # Pre-filter with local classifier to avoid storing irrelevant drafts
        if classify and drafts:
            try:
                from ..classifier import Classifier
                console.print("\n[bold]Running local AI-relevance classifier (Ollama)...[/]")
                clf = Classifier(cfg)
                draft_dicts = [{"name": d.name, "title": d.title, "abstract": d.abstract} for d in drafts]
                relevant, irrelevant = clf.classify_batch(draft_dicts, verbose=True)
                relevant_names = {d["name"] for d in relevant}
                before = len(drafts)
                drafts = [d for d in drafts if d.name in relevant_names]
                console.print(f"\n  Kept [green]{len(drafts)}[/green] / {before} drafts after classification")
                clf.close()
            except Exception as e:
                console.print(f"[yellow]Classifier unavailable ({e}), storing all drafts[/yellow]")

        for draft in drafts:
            db.upsert_draft(draft)
        console.print(f"Stored [bold green]{len(drafts)}[/] drafts in database")

        if download_text:
            missing = db.drafts_without_text()
            if missing:
                console.print(f"Downloading text for [bold]{len(missing)}[/] drafts...")
                texts = fetcher.download_texts(missing)
                for name, text in texts.items():
                    draft = db.get_draft(name)
                    if draft:
                        draft.full_text = text
                        db.upsert_draft(draft)
    finally:
        fetcher.close()


# ── classify ─────────────────────────────────────────────────────────────────


@click.command()
@click.option("--unrated", is_flag=True, help="Classify only unrated drafts")
@click.option("--all", "all_drafts", is_flag=True, help="Classify all drafts (checks accuracy against existing ratings)")
@click.option("--remove", is_flag=True, help="Actually remove drafts classified as irrelevant (use with --unrated)")
@pass_cfg_db
def classify(cfg, db, unrated: bool, all_drafts: bool, remove: bool):
    """Pre-classify drafts as AI-relevant using local Ollama model.

    Runs a two-stage filter (embedding similarity + chat model) to identify
    irrelevant drafts before spending Claude tokens on rating.

    Examples:

        ietf classify --unrated          # preview irrelevant unrated drafts

        ietf classify --unrated --remove  # remove them from DB

        ietf classify --all              # accuracy check against existing ratings
    """
    from ..classifier import Classifier

    clf = Classifier(cfg)

    if all_drafts:
        # Accuracy check mode: compare against existing FP flags
        console.print("[bold]Accuracy check: classifying all rated drafts...[/]\n")
        tp_rows = db.conn.execute(
            "SELECT d.name, d.title, d.abstract FROM drafts d "
            "JOIN ratings r ON d.name = r.draft_name WHERE r.false_positive = 0"
        ).fetchall()
        fp_rows = db.conn.execute(
            "SELECT d.name, d.title, d.abstract FROM drafts d "
            "JOIN ratings r ON d.name = r.draft_name WHERE r.false_positive = 1"
        ).fetchall()

        tp_ok, tp_miss, fp_ok, fp_miss = 0, 0, 0, 0
        for row in tp_rows:
            rel, sim, method = clf.classify(row["title"], row["abstract"])
            if rel:
                tp_ok += 1
            else:
                tp_miss += 1
        for row in fp_rows:
            rel, sim, method = clf.classify(row["title"], row["abstract"])
            if not rel:
                fp_ok += 1
            else:
                fp_miss += 1

        total_tp = len(tp_rows)
        total_fp = len(fp_rows)
        precision = tp_ok / (tp_ok + fp_miss) if (tp_ok + fp_miss) else 0
        recall = tp_ok / total_tp if total_tp else 0
        console.print(f"True Positives:  [green]{tp_ok}[/]/{total_tp} kept ({tp_miss} missed)")
        console.print(f"False Positives: [red]{fp_ok}[/]/{total_fp} filtered ({fp_miss} slipped)")
        console.print(f"Precision: [bold]{precision:.1%}[/]  Recall: [bold]{recall:.1%}[/]")

    elif unrated:
        drafts = db.unrated_drafts(limit=5000)
        if not drafts:
            console.print("No unrated drafts to classify.")
            clf.close()
            return

        console.print(f"[bold]Classifying {len(drafts)} unrated drafts...[/]\n")
        draft_dicts = [{"name": d.name, "title": d.title, "abstract": d.abstract} for d in drafts]
        relevant, irrelevant = clf.classify_batch(draft_dicts, verbose=True)

        if irrelevant:
            console.print(f"\n[bold red]Irrelevant drafts ({len(irrelevant)}):[/]")
            table = Table()
            table.add_column("Name", style="cyan", max_width=50)
            table.add_column("Title", max_width=50)
            for d in irrelevant:
                table.add_row(d["name"], d.get("title", "")[:50])
            console.print(table)

            if remove:
                for d in irrelevant:
                    db.conn.execute("DELETE FROM drafts WHERE name = ?", (d["name"],))
                db.conn.commit()
                console.print(f"\n[bold red]Removed {len(irrelevant)} irrelevant drafts from database[/]")
            else:
                console.print(f"\n[dim]Use --remove to delete these from the DB[/]")
        else:
            console.print("\nAll unrated drafts appear relevant.")
    else:
        console.print("Use --unrated or --all. See: ietf classify --help")

    clf.close()


# ── list ─────────────────────────────────────────────────────────────────────


@click.command("list")
@click.option("--limit", "-n", default=30, help="Number of drafts to show")
@click.option("--sort", "-s", default="time DESC", help="Sort order (e.g. 'time DESC', 'name ASC')")
@pass_cfg_db
def list_drafts(cfg, db, limit: int, sort: str):
    """List tracked drafts."""
    drafts = db.list_drafts(limit=limit, order_by=sort)
    total = db.count_drafts()

    table = Table(title=f"Tracked Drafts ({total} total, showing {len(drafts)})")
    table.add_column("Date", style="dim", width=10)
    table.add_column("Name", style="cyan", max_width=55)
    table.add_column("Title", max_width=50)
    table.add_column("Pg", justify="right", width=4)
    table.add_column("Text", justify="center", width=4)
    table.add_column("Rated", justify="center", width=5)

    for d in drafts:
        has_text = "\u2713" if d.full_text else ""
        rated = "\u2713" if db.get_rating(d.name) else ""
        table.add_row(d.date, d.name, d.title[:50], str(d.pages or ""), has_text, rated)

    console.print(table)


# ── search ───────────────────────────────────────────────────────────────────


@click.command()
@click.argument("query")
@click.option("--limit", "-n", default=20, help="Max results")
@pass_cfg_db
def search(cfg, db, query: str, limit: int):
    """Full-text search across stored drafts."""
    results = db.search_drafts(query, limit=limit)
    if not results:
        console.print(f"No results for [bold]{query}[/]")
        return

    table = Table(title=f"Search: {query} ({len(results)} results)")
    table.add_column("Date", style="dim", width=10)
    table.add_column("Name", style="cyan")
    table.add_column("Title")

    for d in results:
        table.add_row(d.date, d.name, d.title[:60])

    console.print(table)


# ── show ─────────────────────────────────────────────────────────────────────


@click.command()
@click.argument("name")
@pass_cfg_db
def show(cfg, db, name: str):
    """Show detailed info for a draft."""
    from ..reports import Reporter
    from ..readiness import compute_readiness

    reporter = Reporter(cfg, db)
    draft = db.get_draft(name)
    if draft is None:
        console.print(f"[red]Draft not found: {name}[/]")
        return

    rating = db.get_rating(name)

    console.print(f"\n[bold]{draft.title}[/]")
    console.print(f"[dim]{draft.name}[/] rev {draft.rev}  |  {draft.date}  |  {draft.pages or '?'} pages")
    console.print(f"Group: {draft.group or 'individual'}  |  {draft.datatracker_url}")
    console.print(f"\n[italic]{draft.abstract}[/]\n")

    if rating:
        console.print("[bold]AI Assessment[/]")
        console.print(f"  Score: [bold green]{rating.composite_score:.1f}[/]")
        console.print(f"  Summary: {rating.summary}\n")

        table = Table(show_header=True)
        table.add_column("Dimension", width=12)
        table.add_column("Score", justify="center", width=7)
        table.add_column("Notes")
        table.add_row("Novelty", f"{rating.novelty}/5", rating.novelty_note)
        table.add_row("Maturity", f"{rating.maturity}/5", rating.maturity_note)
        table.add_row("Overlap", f"{rating.overlap}/5", rating.overlap_note)
        table.add_row("Momentum", f"{rating.momentum}/5", rating.momentum_note)
        table.add_row("Relevance", f"{rating.relevance}/5", rating.relevance_note)
        console.print(table)

        if rating.categories:
            console.print(f"\nCategories: {', '.join(rating.categories)}")
    else:
        console.print("[dim]Not yet rated — run: ietf analyze {name}[/]")

    # Readiness score
    readiness = compute_readiness(db, name)
    if readiness["score"] > 0:
        console.print(f"\n[bold]Standards Readiness: [cyan]{readiness['score']}/100[/][/]")
        rtable = Table(show_header=True)
        rtable.add_column("Factor", width=20)
        rtable.add_column("Value", justify="center", width=10)
        rtable.add_column("Points", justify="right", width=8)
        rtable.add_column("Detail")
        for key, f in readiness["factors"].items():
            rtable.add_row(f["label"], f"{f['value']:.2f}", f"+{f['contribution']}", f["detail"])
        console.print(rtable)

    # Save detailed report too
    path = reporter.draft_detail(name)
    if path:
        console.print(f"\n[dim]Report saved: {path}[/]")


# ── annotate ─────────────────────────────────────────────────────────────────


@click.command()
@click.argument("draft_name")
@click.option("--note", "-n", default=None, help="Set/update the note text")
@click.option("--tag", "-t", multiple=True, help="Add a tag (can be used multiple times)")
@click.option("--remove-tag", "-r", multiple=True, help="Remove a tag (can be used multiple times)")
@pass_cfg_db
def annotate(cfg, db, draft_name: str, note: str | None, tag: tuple[str, ...], remove_tag: tuple[str, ...]):
    """Add or view annotations (notes & tags) for a draft."""
    draft = db.get_draft(draft_name)
    if draft is None:
        console.print(f"[red]Draft not found: {draft_name}[/]")
        return

    # If no options, display current annotation
    if note is None and not tag and not remove_tag:
        ann = db.get_annotation(draft_name)
        if ann:
            console.print(f"\n[bold]Annotation for {draft_name}[/]")
            console.print(f"  Note: {ann['note'] or '(empty)'}")
            console.print(f"  Tags: {', '.join(ann['tags']) if ann['tags'] else '(none)'}")
            console.print(f"  Updated: {ann['updated_at']}")
        else:
            console.print(f"[dim]No annotation for {draft_name}. Use --note or --tag to add one.[/]")
        return

    # Fetch existing tags for add/remove operations
    existing = db.get_annotation(draft_name)
    current_tags = existing["tags"] if existing else []

    for t in tag:
        if t not in current_tags:
            current_tags.append(t)
    for t in remove_tag:
        if t in current_tags:
            current_tags.remove(t)

    db.upsert_annotation(draft_name, note=note, tags=current_tags)
    ann = db.get_annotation(draft_name)
    console.print(f"[green]Annotation updated for {draft_name}[/]")
    console.print(f"  Note: {ann['note'] or '(empty)'}")
    console.print(f"  Tags: {', '.join(ann['tags']) if ann['tags'] else '(none)'}")


# ── authors ─────────────────────────────────────────────────────────────


@click.command()
@click.argument("name", required=False)
@click.option("--fetch/--no-fetch", default=False, help="Fetch author data from Datatracker first")
@click.option("--limit", "-n", default=20, help="Number of top authors to show")
@pass_cfg_db
def authors(cfg, db, name: str | None, fetch: bool, limit: int):
    """Show authors for a draft, or top authors overall."""
    from ..authors import AuthorNetwork

    author_network = AuthorNetwork(cfg, db)

    if fetch:
        count = author_network.fetch_all_authors()
        console.print(f"Fetched authors for [bold green]{count}[/] drafts")

    if name:
        draft_authors = db.get_authors_for_draft(name)
        if not draft_authors:
            console.print(f"[yellow]No author data for {name}. Run `ietf authors --fetch` first.[/]")
            return
        console.print(f"\n[bold]Authors of {name}:[/]")
        for a in draft_authors:
            console.print(f"  - {a.name} ({a.affiliation or 'no affiliation'})")
    else:
        top = db.top_authors(limit=limit)
        if not top:
            console.print("[yellow]No author data. Run `ietf authors --fetch` first.[/]")
            return
        table = Table(title=f"Top {limit} Authors")
        table.add_column("#", justify="right", width=4)
        table.add_column("Author", style="cyan")
        table.add_column("Organization")
        table.add_column("Drafts", justify="right", width=6)
        for rank, (aname, aff, cnt, _, _pid) in enumerate(top, 1):
            table.add_row(str(rank), aname, aff, str(cnt))
        console.print(table)


@click.command()
@click.option("--top", "-n", default=20, help="Top N to show")
@pass_cfg_db
def network(cfg, db, top: int):
    """Show author collaboration network."""
    console.print("\n[bold]Top Organizations[/]")
    orgs = db.top_orgs(limit=top)
    if orgs:
        table = Table()
        table.add_column("#", justify="right", width=4)
        table.add_column("Organization", style="cyan")
        table.add_column("Authors", justify="right", width=8)
        table.add_column("Drafts", justify="right", width=6)
        for rank, (org, auth_cnt, draft_cnt) in enumerate(orgs, 1):
            table.add_row(str(rank), org, str(auth_cnt), str(draft_cnt))
        console.print(table)

    console.print("\n[bold]Cross-Org Collaboration[/]")
    cross = db.cross_org_collaborations(limit=top)
    if cross:
        table = Table()
        table.add_column("Org A", style="cyan")
        table.add_column("Org B", style="cyan")
        table.add_column("Shared Drafts", justify="right", width=8)
        for org_a, org_b, shared in cross:
            table.add_row(org_a, org_b, str(shared))
        console.print(table)
    else:
        console.print("[yellow]No author data. Run `ietf authors --fetch` first.[/]")
