# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "click>=8.4.1",
#     "rich>=15.0.0",
#     "polars>=1.40.1",
#     "rustworkx>=0.17.1"
# ]
# ///
import sys, io
import click

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

import polars as pl
from smithy import smith_set, irv_set


@click.command()
@click.argument("ballots", type=click.Path(exists=True, dir_okay=False))
@click.option(
    "--try-resolve-irv",
    is_flag=True,
    help="Try to reduce or resolve the Smith set by running all-paths IRV on the set.",
)
@click.option(
    "--show-ballots",
    "-b",
    is_flag=True,
    help="Show relevant ballots (after selections).",
)
@click.option("--pretty", "-p", is_flag=True, help="Pretty-print output.")
def cli(ballots: str, try_resolve_irv=False, show_ballots=False, pretty=False) -> None:
    """
    Compute the Smith set from a box of ranked-choice ballots -- .csv or .xls(x).

    The Smith set is the minimal set of candidates which can beat all others pairwise
    (simple ranking majority) - if there is a single winner in the set,
    they are guaranteed the Condorcet i.e. Majority winner.
    """

    console = Console()

    try:
        # Load ballots
        if ballots.endswith(".csv"):
            df = pl.read_csv(ballots)

        elif ballots.endswith((".xlsx", ".xls")):
            df = pl.read_excel(ballots)

        else:
            console.print(
                "[bold red]Unsupported file type.[/bold red]\nUse CSV or Excel."
            )
            raise SystemExit(1)

        # Normalize numerical dataframe entries
        df = df.with_columns(
            [
                pl.col(c)
                .cast(pl.String)
                .str.strip_chars()
                .cast(pl.Int64, strict=False)
                .fill_null(0)
                for c in df.columns
            ]
        )

        # Compute Smith set
        smiths = smith_set(df)
        if len(smiths) > 1 and try_resolve_irv:
            irv_ballots = df.select(smiths)
            smiths = irv_set(irv_ballots)

        if show_ballots and pretty:
            preview = Table(title="Ballot Box")

            for col in df.columns:
                preview.add_column(col)

            for row in df.head(5).iter_rows():
                preview.add_row(*map(str, row))

            console.print(preview)
            console.print()
        elif show_ballots and not pretty:
            buf = io.StringIO()
            df.write_csv(buf)
            console.print(buf.getvalue())
            console.print("---")

        if pretty:
            console.print(
                Panel.fit(
                    "\n".join(f"• {c}" for c in smiths),
                    title="Resulting IRV-resolved Smith Set"
                    if (try_resolve_irv)
                    else "Resulting Smith Set",
                    border_style="green",
                )
            )
        else:
            console.print(", ".join(f"{c}" for c in smiths))

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")

        raise SystemExit(1)


if __name__ == "__main__":
    cli()
