from __future__ import annotations

import argparse
import csv
import json
import sys
from collections import OrderedDict
from pathlib import Path


FILE = Path(__file__).resolve()
DEFAULT_INPUT_FILE = FILE.parents[1] / "examples" / "events" / "G1Q3_场地评测数据集.csv"
DEFAULT_SCENE_COLUMN = "scene"


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Parse a CSV table and save a scene-keyed JSON file."
    )
    parser.add_argument(
        "--input-file",
        type=str,
        default=str(DEFAULT_INPUT_FILE),
        help="Path to the input CSV file.",
    )
    parser.add_argument(
        "--scene-column",
        type=str,
        default=DEFAULT_SCENE_COLUMN,
        help="Column name used as the top-level JSON key.",
    )
    parser.add_argument(
        "--output-file",
        type=str,
        default="",
        help="Optional output JSON path. Defaults to a sibling .json file next to the CSV.",
    )
    parser.add_argument(
        "--keep-scene-field",
        action="store_true",
        help="Keep the scene column inside each grouped record.",
    )
    return parser.parse_args()


def normalize_row(row: dict[str, str | None]) -> dict[str, str]:
    return {str(key).strip(): str(value or "").strip() for key, value in row.items() if key is not None}


def resolve_output_path(input_path: Path, output_file: str) -> Path:
    if output_file:
        return Path(output_file)
    return input_path.with_suffix(".json")


def load_csv_rows(path: Path) -> tuple[list[str], list[dict[str, str]]]:
    with path.open("r", encoding="utf-8-sig", newline="") as file:
        sample = file.read(4096)
        file.seek(0)
        dialect = csv.Sniffer().sniff(sample) if sample.strip() else csv.excel
        reader = csv.DictReader(file, dialect=dialect)
        headers = [str(header).strip() for header in (reader.fieldnames or [])]
        rows: list[dict[str, str]] = []
        for row in reader:
            normalized = normalize_row(row)
            if any(normalized.values()):
                rows.append(normalized)
    return headers, rows


def group_rows_by_scene(
    rows: list[dict[str, str]],
    scene_column: str,
    keep_scene_field: bool,
) -> OrderedDict[str, list[dict[str, str]]]:
    grouped: OrderedDict[str, list[dict[str, str]]] = OrderedDict()
    missing_scene_rows = 0

    for row in rows:
        scene_value = row.get(scene_column, "").strip()
        if not scene_value:
            missing_scene_rows += 1
            continue

        payload = dict(row)
        if not keep_scene_field:
            payload.pop(scene_column, None)

        grouped.setdefault(scene_value, []).append(payload)

    if missing_scene_rows > 0:
        print(
            f"Skipped {missing_scene_rows} rows because column {scene_column!r} was empty.",
            file=sys.stderr,
        )

    return grouped


def save_grouped_json(grouped: OrderedDict[str, list[dict[str, str]]], output_path: Path) -> None:
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(
        json.dumps(grouped, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )


def main() -> int:
    args = parse_args()
    input_path = Path(args.input_file)
    if not input_path.is_file():
        raise FileNotFoundError(f"Input file not found: {input_path}")

    headers, rows = load_csv_rows(input_path)
    if args.scene_column not in headers:
        available = ", ".join(headers)
        raise ValueError(f"Column {args.scene_column!r} not found. Available columns: {available}")

    grouped = group_rows_by_scene(rows, args.scene_column, args.keep_scene_field)
    output_path = resolve_output_path(input_path, args.output_file)
    save_grouped_json(grouped, output_path)

    print(
        f"Saved {len(rows)} rows across {len(grouped)} scenes to {output_path}",
        file=sys.stderr,
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
