import json
import os

import typer

app = typer.Typer(help="PostgreSQL utilities")


def get_connection():
    try:
        import psycopg
    except ImportError:
        raise typer.Exit("psycopg not installed. Run: uvx 'adaptivegears[pg]' pg ...")

    if not os.environ.get("PGDATABASE"):
        raise typer.Exit("PGDATABASE environment variable not set")

    return psycopg.connect()


def glob_to_sql_like(pattern: str) -> str:
    """Convert shell glob pattern to SQL LIKE pattern."""
    return pattern.replace("*", "%").replace("?", "_")


def format_size(bytes_val: int | None) -> str:
    """Human-readable size."""
    if bytes_val is None or bytes_val == 0:
        return "0 B"
    for unit in ("B", "kB", "MB", "GB", "TB"):
        if abs(bytes_val) < 1024:
            return f"{bytes_val:.0f} {unit}" if unit == "B" else f"{bytes_val:.1f} {unit}"
        bytes_val /= 1024
    return f"{bytes_val:.1f} PB"


@app.command("ping")
def ping():
    """Check database connectivity."""
    with get_connection() as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT 1")
    print("PONG")


@app.command("list")
def list_tables(
    pattern: str = typer.Argument(None, help="Table name pattern (glob: page_schema_*)"),
    schema: str = typer.Option("public", "--schema", "-s", help="Schema to list tables from"),
    min_rows: int = typer.Option(None, "--min-rows", help="Only tables with at least N rows"),
    max_rows: int = typer.Option(None, "--max-rows", help="Only tables with at most N rows"),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
):
    """List tables with size statistics."""
    like_pattern = glob_to_sql_like(pattern) if pattern else "%"

    with get_connection() as conn:
        with conn.cursor() as cur:
            cur.execute(
                """
                SELECT
                    c.relname AS table,
                    c.reltuples::bigint AS rows,
                    pg_total_relation_size(c.oid) AS total_bytes
                FROM pg_class c
                JOIN pg_namespace n ON n.oid = c.relnamespace
                WHERE n.nspname = %s
                  AND c.relkind = 'r'
                  AND c.relname LIKE %s
                ORDER BY c.relname
                """,
                (schema, like_pattern),
            )
            rows = cur.fetchall()

    tables = []
    for table, row_count, total_bytes in rows:
        avg_bytes = total_bytes // row_count if row_count > 0 else 0
        tables.append({
            "table": table,
            "rows": row_count,
            "total_size": total_bytes,
            "avg_row_size": avg_bytes,
        })

    # Filter by row count
    if min_rows is not None:
        tables = [t for t in tables if t["rows"] >= min_rows]
    if max_rows is not None:
        tables = [t for t in tables if t["rows"] <= max_rows]

    if json_output:
        print(json.dumps(tables))
    else:
        if not tables:
            return
        name_width = max(len(t["table"]) for t in tables)
        rows_width = max(len(f"{t['rows']:,}") for t in tables)
        for t in tables:
            print(
                f"{t['table']:<{name_width}}  "
                f"{t['rows']:>{rows_width},}  "
                f"{format_size(t['total_size']):>10}  "
                f"{format_size(t['avg_row_size']):>10}"
            )
