"""The indexing commands (Stream K6; plan W18): `index suggest`, `index list`, `index approve`,
`index remove`. The same service the `/review` page's index candidates panel calls
(`services/publish.py`), so a page approved here and one approved there are one record: a
`decisions` row on the page's `indexed` field, in a batch a person can undo
(`decisions undo-batch <uid>`).

A page is named `<kind>:<slug or id>`: `brand:macallan`, `line:macallan-double-cask-12`,
`place:heathrow-lhr-london`, or `airport:LHR`. An old slug names the page it forwards to.

Sources of truth: this module, `services/publish.py`, `docs/RUNBOOK.md`.
"""

from __future__ import annotations

import argparse

from app.config import settings
from app.db import SessionLocal
from app.services import indexnow, publish

_KINDS = {"brand": "brand", "line": "product_line", "product_line": "product_line", "place": "place", "airport": "place"}


def _pages(db, names: list[str]) -> list[tuple[str, str | int]]:
    out: list[tuple[str, str | int]] = []
    for name in names:
        word, _, ref = name.partition(":")
        kind = _KINDS.get(word)
        if kind is None or not ref:
            raise SystemExit(f"index: {name!r} is not <kind>:<slug>; kinds are brand, line, place, airport")
        if word == "airport":
            place = publish.place_by_identifier(db, "iata", ref)
            ref = place.id if place is not None else ref
        out.append((kind, ref))
    return out


def cmd_suggest(args: argparse.Namespace) -> int:
    with SessionLocal() as db:
        counts = publish.suggest(db, check=args.check)
        if not args.check:
            db.commit()
    print(f"index suggest{' --check (nothing written)' if args.check else ''}: " + ", ".join(f"{k} {v}" for k, v in counts.items()))
    return 0


def cmd_list(args: argparse.Namespace) -> int:
    with SessionLocal() as db:
        desk = publish.desk(db, kind=_KINDS.get(args.kind) if args.kind else None, limit=args.limit)
    print("index list: waiting " + ", ".join(f"{k} {v}" for k, v in desk["totals"].items()))
    for row in desk["rows"]:
        quality = {True: "reviewed", False: "not reviewed", None: "no sheet"}[row["quality"]]
        print(f"  {row['action']:8} {row['kind']}:{row['slug']}  [{quality}; {'indexed' if row['indexed'] else 'noindex'}]  {row['why']}")
    return 0


def _decide(args: argparse.Namespace, indexed: bool) -> int:
    with SessionLocal() as db:
        if args.all_suggested:
            reason = publish.REASON_ADD if indexed else publish.REASON_REMOVE
            pages = [(publish.KIND_OF_LEVEL[s.level], s.left_id) for s in publish.open_suggestions(db) if s.reason == reason]
        else:
            pages = _pages(db, args.pages)
        if not pages:
            print("index: no page named, and nothing suggested")
            return 0
        result = publish.set_indexed(db, pages, indexed, args.by, batch_kind="cli", reason=args.reason)
        db.commit()
    print(f"index {'approve' if indexed else 'remove'}: {result['message']}"
          + (f"; undo with `decisions undo-batch {result['batch_uid']}`" if result["batch_uid"] else ""))
    for r in result["refusals"]:
        print(f"  refused {r['page']}: {r['error_code']}: {r['summary']}")
    if result["ping"]:
        sent = indexnow.ping_approved(result["ping"], settings.public_base_url.rstrip("/"), settings.indexnow_key if settings.has_indexnow else "")
        print(f"  IndexNow: {'pinged ' + str(sum(n for n, _ in sent)) + ' URL(s)' if sent else 'not configured on this host; nothing sent'}")
    return 2 if result["refusals"] and not result["counts"]["changed"] else 0


def register(sub: argparse._SubParsersAction) -> None:
    index = sub.add_parser("index", help="which pages search engines are shown: suggest, list, approve, remove")
    isub = index.add_subparsers(dest="index_command", required=True)

    sg = isub.add_parser("suggest", help="write the pages that meet their rule as suggestions to index (idempotent; applies nothing)")
    sg.add_argument("--check", action="store_true", help="count only; write nothing")
    sg.set_defaults(func=cmd_suggest)

    ls = isub.add_parser("list", help="read-only: the suggestions waiting, with each page's facts")
    ls.add_argument("--kind", choices=sorted(_KINDS))
    ls.add_argument("--limit", type=int, default=50)
    ls.set_defaults(func=cmd_list)

    for name, indexed, text in (("approve", True, "approve pages for indexing (one batch, one undo)"),
                                ("remove", False, "remove pages from the index (one batch, one undo)")):
        cmd = isub.add_parser(name, help=text)
        cmd.add_argument("pages", nargs="*", metavar="KIND:SLUG", help="brand:<slug>, line:<slug>, place:<slug> or airport:<IATA>")
        cmd.add_argument("--all-suggested", action="store_true", help="every page with an open suggestion of this kind of action")
        cmd.add_argument("--by", required=True, help="the account username the decision is recorded against")
        cmd.add_argument("--reason")
        cmd.set_defaults(func=lambda a, indexed=indexed: _decide(a, indexed))
