"""Shelf-space report over the box dimensions enrich collected.

Reads games.json only — fully offline. The fit target is an IKEA Kallax
cube: a box fits if SOME orientation puts two axes through the ~13.2"
square opening with the third axis within the ~15.4" depth. "Can't
verify" is reported as exactly that, never as a fit.
"""

from __future__ import annotations

import json
from itertools import permutations

import typer

from bggpipe.config import Config

KALLAX_OPENING_IN = 13.2
KALLAX_DEPTH_IN = 15.4

_AXES = ("width_in", "length_in", "depth_in")


def fits_kallax(width: float, length: float, depth: float) -> bool:
    return any(
        a <= KALLAX_OPENING_IN and b <= KALLAX_OPENING_IN and c <= KALLAX_DEPTH_IN
        for a, b, c in permutations((width, length, depth))
    )


def _footprint(dims: dict) -> float:
    """The two largest axes multiplied — the shelf area a box claims
    lying in its flattest orientation."""
    axes = sorted((dims[a] for a in _AXES), reverse=True)
    return axes[0] * axes[1]


def run_dims_report(cfg: Config) -> dict:
    if not cfg.games_path.exists():
        typer.echo(f"{cfg.games_path} not found — run `bggpipe enrich` first.")
        raise typer.Exit(code=1)
    games: dict[str, dict] = json.loads(cfg.games_path.read_text())

    contained = [
        entry.get("name") or "?" for entry in games.values() if entry.get("stored_in")
    ]
    by_source: dict[str, list[tuple[str, dict]]] = {}
    for entry in games.values():
        if entry.get("stored_in"):
            continue  # lives inside another box: no shelf space of its own
        dims = entry.get("dims") or dict.fromkeys(_AXES, None) | {"source": "absent"}
        by_source.setdefault(dims["source"], []).append(
            (entry.get("name") or "?", dims)
        )

    typer.echo(f"Box dimensions across {len(games)} game(s):")
    if contained:
        typer.echo(
            f"  {len(contained):>4}  stored inside another box — no shelf "
            "space of their own"
        )
    for source, label in (
        ("version", "from the exact owned version"),
        ("unanimous", "every listed version agrees"),
        ("conflicting", "versions disagree — not guessed"),
        ("absent", "no data on BGG (or no BGG entry)"),
    ):
        if by_source.get(source):
            typer.echo(f"  {len(by_source[source]):>4}  {source} — {label}")

    measured = sorted(
        (
            (name, dims)
            for source in ("version", "unanimous")
            for name, dims in by_source.get(source, [])
        ),
        key=lambda item: _footprint(item[1]),
        reverse=True,
    )
    if measured:
        typer.echo("\nBiggest footprints (two largest axes, flattest lie):")
        for name, dims in measured[:10]:
            w, length, d = (dims[a] for a in _AXES)
            typer.echo(
                f"  {_footprint(dims):>6.1f} sq in  {name}"
                f"  ({w:g} × {length:g} × {d:g} in)"
            )

    misfits = [
        (name, dims)
        for name, dims in measured
        if not fits_kallax(*(dims[a] for a in _AXES))
    ]
    unknown = [
        name
        for source in ("conflicting", "absent")
        for name, _ in by_source.get(source, [])
    ]
    typer.echo(
        f'\nKallax check ({KALLAX_OPENING_IN:g}" × {KALLAX_OPENING_IN:g}" opening, '
        f'{KALLAX_DEPTH_IN:g}" deep):'
    )
    if misfits:
        typer.echo(f"  {len(misfits)} box(es) do NOT fit in any orientation:")
        for name, dims in misfits:
            w, length, d = (dims[a] for a in _AXES)
            typer.echo(f"    {name}  ({w:g} × {length:g} × {d:g} in)")
    else:
        typer.echo("  every measured box fits.")
    if unknown:
        typer.echo(
            f"  {len(unknown)} game(s) can't be verified (no trusted "
            "dimensions — not the same as fitting):"
        )
        for name in sorted(unknown):
            typer.echo(f"    {name}")

    return {
        "by_source": {s: len(v) for s, v in by_source.items()},
        "misfits": [name for name, _ in misfits],
        "unknown": sorted(unknown),
        "contained": sorted(contained),
    }
