"""Unified output renderer for worai commands.

Usage::

    from worai.core.output import OutputFormat, write_output

    write_output(rows, OutputFormat.csv, dest=Path("out.csv"))
    write_output(rows, OutputFormat.table)           # → stdout rich table
    write_output(rows, OutputFormat.json)            # → stdout JSON array
    write_output(rows, OutputFormat.parquet, dest=Path("out.parquet"))
    write_output(
        rows, OutputFormat.gsheets,
        credentials_file="svc.json",
        spreadsheet_id="<id>",
        sheet_name="Sheet1",
    )
"""

from __future__ import annotations

import csv
import json
import sys
from enum import Enum
from pathlib import Path
from typing import Any


class OutputFormat(str, Enum):
    table = "table"
    json = "json"
    csv = "csv"
    tsv = "tsv"
    parquet = "parquet"
    gsheets = "gsheets"


def write_output(
    rows: list[dict],
    fmt: OutputFormat,
    dest: str | Path | None = None,
    **fmt_options: Any,
) -> None:
    """Write *rows* in the requested *fmt* to *dest* (stdout when None).

    Args:
        rows:        Uniformly-keyed dicts; column order is inferred from the
                     first row's key order.
        fmt:         One of the :class:`OutputFormat` values.
        dest:        ``None`` → stdout (not valid for ``parquet``);
                     path string or :class:`~pathlib.Path` → write to file.
        **fmt_options:
            ``no_color``        (table)  – strip ANSI codes; default False.
            ``credentials_file`` (gsheets) – service-account JSON path.
            ``spreadsheet_id``   (gsheets) – Google Sheets document ID.
            ``sheet_name``       (gsheets) – worksheet tab name.
    """
    if fmt == OutputFormat.table:
        _write_table(rows, dest, no_color=fmt_options.get("no_color", False))
    elif fmt == OutputFormat.json:
        _write_json(rows, dest)
    elif fmt == OutputFormat.csv:
        _write_separated(rows, dest, delimiter=",")
    elif fmt == OutputFormat.tsv:
        _write_separated(rows, dest, delimiter="\t")
    elif fmt == OutputFormat.parquet:
        _write_parquet(rows, dest)
    elif fmt == OutputFormat.gsheets:
        _write_gsheets(rows, **fmt_options)
    else:
        raise ValueError(f"Unsupported output format: {fmt!r}")


# ---------------------------------------------------------------------------
# Format handlers
# ---------------------------------------------------------------------------


def _write_table(rows: list[dict], dest: str | Path | None, *, no_color: bool = False) -> None:
    from rich.console import Console
    from rich.table import Table

    if not rows:
        return

    if dest is not None:
        f = open(dest, "w", encoding="utf-8")
        console = Console(file=f, no_color=True)
    else:
        console = Console(file=sys.stdout, no_color=no_color)

    columns = list(rows[0].keys())
    table = Table(*columns)
    for row in rows:
        table.add_row(*[str(row.get(col, "")) for col in columns])

    try:
        console.print(table)
    finally:
        if dest is not None:
            f.close()


def _write_json(rows: list[dict], dest: str | Path | None) -> None:
    text = json.dumps(rows, indent=2)
    if dest is None:
        sys.stdout.write(text + "\n")
    else:
        Path(dest).write_text(text, encoding="utf-8")


def _write_separated(rows: list[dict], dest: str | Path | None, *, delimiter: str) -> None:
    fieldnames: list[str] = list(rows[0].keys()) if rows else []

    if dest is None:
        writer = csv.DictWriter(sys.stdout, fieldnames=fieldnames, delimiter=delimiter)
        writer.writeheader()
        writer.writerows(rows)
    else:
        with open(dest, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter=delimiter)
            writer.writeheader()
            writer.writerows(rows)


def _write_parquet(rows: list[dict], dest: str | Path | None) -> None:
    if dest is None:
        raise ValueError(
            "parquet format requires a destination file path "
            "(pass --output / dest= to write_output)."
        )
    try:
        import pyarrow as pa
        import pyarrow.parquet as pq
    except ImportError:
        raise ImportError(
            "pyarrow is required for parquet output. "
            "Install it with: pip install 'worai[parquet]'"
        ) from None

    table = pa.Table.from_pylist(rows)
    pq.write_table(table, str(dest))


def _write_gsheets(rows: list[dict], **fmt_options: Any) -> None:
    credentials_file = fmt_options.get("credentials_file")
    spreadsheet_id = fmt_options.get("spreadsheet_id")
    sheet_name = fmt_options.get("sheet_name")

    missing = [k for k, v in [
        ("credentials_file", credentials_file),
        ("spreadsheet_id", spreadsheet_id),
        ("sheet_name", sheet_name),
    ] if not v]
    if missing:
        raise ValueError(
            f"gsheets format requires fmt_options: {', '.join(missing)}"
        )

    try:
        import gspread
    except ImportError:
        raise ImportError(
            "gspread is required for gsheets output. "
            "Install it with: pip install 'worai[gsheets]'"
        ) from None

    gc = gspread.service_account(filename=credentials_file)
    spreadsheet = gc.open_by_key(spreadsheet_id)

    try:
        worksheet = spreadsheet.worksheet(sheet_name)
    except gspread.exceptions.WorksheetNotFound:
        cols = len(rows[0]) if rows else 1
        worksheet = spreadsheet.add_worksheet(
            title=sheet_name, rows=len(rows) + 1, cols=cols
        )

    worksheet.clear()

    if not rows:
        return

    headers = list(rows[0].keys())
    data = [headers] + [[row.get(col, "") for col in headers] for row in rows]
    worksheet.update(data)
