#!/usr/bin/env python3
"""Flip a tenant's books ownership (ADR #021).

    python -m scripts.flip_books_owner --project-tenant plusroi --to app
    python -m scripts.flip_books_owner --project-tenant plusroi --to sheet  # revert

Sets ``tenants.settings.books_owner`` on the tenant whose org party is
named ``--project-tenant`` (case-insensitive), preserving every other
setting (bookkeeping_pct, partner_split, default_currency,
workspace_profile, …) and dropping the superseded ``parallel_run``
boolean. Idempotent: re-running with the same target is a no-op.

  - ``--to app``:   the nightly rebuild LEAVES this tenant untouched
                    (tenant-scoped reset + sheet-step skip) and in-app
                    invoice/expense writes are ALLOWED. Flip only right
                    after a fresh final import — the app becomes the
                    system of record from that seed.
  - ``--to sheet``: the nightly reimports the books from the sheet
                    export and in-app writes are refused. Reverting to
                    sheet WIPES any rows written in-app on the next run
                    (that is what sheet ownership means) — hence
                    --confirm-wipe is required for --to sheet.

Without --dsn the sidecar URL comes from the environment/.env
(WITH_DB_URL in-container, WITH_DB_URL_HOST host-side).
"""

from __future__ import annotations

import argparse
import json
import sys

from sqlalchemy import Engine, create_engine, text

from app.services.tenancy import BOOKS_OWNER_APP, BOOKS_OWNER_SHEET


def flip_books_owner(engine: Engine, tenant_party_name: str, to: str) -> dict:
    """Set the tenant's ``settings.books_owner`` (dropping ``parallel_run``).

    Returns {tenant_id, tenant, before, after, changed}. Raises
    LookupError when no tenant's org party matches the name.
    """
    if to not in (BOOKS_OWNER_SHEET, BOOKS_OWNER_APP):
        raise ValueError(f"--to must be 'sheet' or 'app', not {to!r}")
    with engine.begin() as conn:
        row = conn.execute(
            text(
                "SELECT t.id, p.name, t.settings FROM tenants t"
                " JOIN parties p ON p.id = t.party_id"
                " WHERE lower(p.name) = lower(:name)"
            ),
            {"name": tenant_party_name},
        ).fetchone()
        if row is None:
            raise LookupError(
                f"no tenant whose org party is named {tenant_party_name!r}"
            )
        tenant_id, party_name, settings = row[0], row[1], dict(row[2] or {})
        before = dict(settings)
        settings.pop("parallel_run", None)  # superseded by books_owner
        settings["books_owner"] = to
        changed = settings != before
        if changed:
            conn.execute(
                text(
                    "UPDATE tenants SET settings = cast(:settings AS jsonb)"
                    " WHERE id = :id"
                ),
                {"id": tenant_id, "settings": json.dumps(settings)},
            )
    return {
        "tenant_id": str(tenant_id),
        "tenant": party_name,
        "before": before,
        "after": settings,
        "changed": changed,
    }


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(
        description="Flip a tenant's books ownership (sheet <-> app)."
    )
    parser.add_argument(
        "--project-tenant",
        required=True,
        help="the tenant's org party name (case-insensitive), e.g. plusroi",
    )
    parser.add_argument("--to", required=True, choices=("sheet", "app"))
    parser.add_argument("--dsn", help="target Postgres DSN (else WITH_DB_URL[_HOST])")
    parser.add_argument(
        "--confirm-wipe",
        action="store_true",
        help="required for --to sheet: the next nightly WIPES in-app rows",
    )
    args = parser.parse_args(argv)

    if args.to == "sheet" and not args.confirm_wipe:
        print(
            "refusing: --to sheet makes the next nightly reimport WIPE any"
            " rows written in the app. Re-run with --confirm-wipe if that is"
            " intended.",
            file=sys.stderr,
        )
        return 1

    from app.services.migrate.transforms import _default_url

    engine = create_engine(args.dsn or _default_url(), pool_pre_ping=True)
    try:
        result = flip_books_owner(engine, args.project_tenant, args.to)
    except LookupError as exc:
        print(f"error: {exc}", file=sys.stderr)
        return 1
    finally:
        engine.dispose()
    print(json.dumps(result, indent=2, default=str))
    if result["changed"]:
        print(
            f"flipped {result['tenant']} -> books_owner={args.to}", file=sys.stderr
        )
    else:
        print(
            f"no-op: {result['tenant']} already books_owner={args.to}",
            file=sys.stderr,
        )
    return 0


if __name__ == "__main__":
    sys.exit(main())
