"""Pandas DataFrame output writer."""

from __future__ import annotations

from pathlib import Path

import pandas as pd


class PandasWriter:
    """Write generated tables to various local formats."""

    def to_csv(
        self,
        tables: dict[str, pd.DataFrame],
        output_dir: str | Path,
        separator: str = ",",
    ) -> list[Path]:
        """Write all tables as CSV files."""
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        written = []
        for table_name, df in tables.items():
            path = output_dir / f"{table_name}.csv"
            df.to_csv(path, index=False, sep=separator)
            written.append(path)

        return written

    def to_tsv(
        self,
        tables: dict[str, pd.DataFrame],
        output_dir: str | Path,
    ) -> list[Path]:
        """Write all tables as tab-delimited text files."""
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        written = []
        for table_name, df in tables.items():
            path = output_dir / f"{table_name}.tsv"
            df.to_csv(path, index=False, sep="\t")
            written.append(path)

        return written

    def to_jsonl(
        self,
        tables: dict[str, pd.DataFrame],
        output_dir: str | Path,
    ) -> list[Path]:
        """Write all tables as JSON Lines files (one JSON object per line)."""
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        written = []
        for table_name, df in tables.items():
            path = output_dir / f"{table_name}.jsonl"
            df.to_json(path, orient="records", lines=True, date_format="iso")
            written.append(path)

        return written

    def to_parquet(
        self,
        tables: dict[str, pd.DataFrame],
        output_dir: str | Path,
    ) -> list[Path]:
        """Write all tables as Parquet files (requires pyarrow)."""
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        written = []
        for table_name, df in tables.items():
            path = output_dir / f"{table_name}.parquet"
            df.to_parquet(path, index=False)
            written.append(path)

        return written

    def to_excel(
        self,
        tables: dict[str, pd.DataFrame],
        output_dir: str | Path,
        single_workbook: bool = True,
    ) -> list[Path]:
        """Write tables as Excel files (requires openpyxl).

        If single_workbook=True, writes one .xlsx with a sheet per table.
        If False, writes one .xlsx per table.
        """
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        written = []
        if single_workbook:
            path = output_dir / "spindle_output.xlsx"
            with pd.ExcelWriter(path, engine="openpyxl") as writer:
                for table_name, df in tables.items():
                    # Excel sheet names max 31 chars
                    sheet = table_name[:31]
                    df.to_excel(writer, sheet_name=sheet, index=False)
            written.append(path)
        else:
            for table_name, df in tables.items():
                path = output_dir / f"{table_name}.xlsx"
                df.to_excel(path, index=False, engine="openpyxl")
                written.append(path)

        return written

    def to_sql_inserts(
        self,
        tables: dict[str, pd.DataFrame],
        output_dir: str | Path,
        schema_name: str | None = None,
        batch_size: int = 1000,
    ) -> list[Path]:
        """Write tables as SQL INSERT scripts.

        Generates standard SQL INSERT statements compatible with SQL Server,
        PostgreSQL, and most ANSI SQL databases.
        """
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        written = []
        for table_name, df in tables.items():
            path = output_dir / f"{table_name}.sql"
            prefix = f"[{schema_name}]." if schema_name else ""
            qualified = f"{prefix}[{table_name}]"
            columns = ", ".join(f"[{c}]" for c in df.columns)

            with open(path, "w", encoding="utf-8") as f:
                f.write(f"-- Generated by Spindle (sqllocks-spindle)\n")
                f.write(f"-- Table: {table_name} ({len(df)} rows)\n\n")

                for batch_start in range(0, len(df), batch_size):
                    batch = df.iloc[batch_start : batch_start + batch_size]
                    f.write(f"INSERT INTO {qualified} ({columns})\nVALUES\n")

                    rows = []
                    for _, row in batch.iterrows():
                        vals = []
                        for val in row:
                            if pd.isna(val):
                                vals.append("NULL")
                            elif isinstance(val, str):
                                escaped = val.replace("'", "''")
                                vals.append(f"'{escaped}'")
                            elif isinstance(val, bool):
                                vals.append("1" if val else "0")
                            elif isinstance(val, (int, float)):
                                vals.append(str(val))
                            else:
                                escaped = str(val).replace("'", "''")
                                vals.append(f"'{escaped}'")
                        rows.append(f"  ({', '.join(vals)})")

                    f.write(",\n".join(rows))
                    f.write(";\n\n")

            written.append(path)

        return written
