"""
CSV merge helpers for g-gremlin.
"""

from __future__ import annotations

import csv
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Set


@dataclass
class MergeResult:
    """
    Summary of a merge operation.
    """

    rows_written: int
    columns: List[str]
    warnings: List[str]


def merge_csv_sources(
    sources: List[Path],
    key: str,
    output_path: Path,
    source_flag_col: Optional[str] = None,
) -> MergeResult:
    """
    Merge multiple CSVs on a key, unioning columns and deduplicating by key.

    Rows with missing keys are retained with synthetic keys and reported via warnings.
    For duplicate keys, the first non-empty value wins for each column.
    """
    if not sources:
        raise ValueError("At least one source CSV is required.")
    columns: List[str] = []
    merged_rows: Dict[str, Dict[str, str]] = {}
    sources_by_key: Dict[str, Set[str]] = {}
    warnings: List[str] = []
    missing_key_count = 0

    for src in sources:
        with src.open("r", newline="", encoding="utf-8-sig") as handle:
            reader = csv.DictReader(handle)
            if reader.fieldnames is None:
                raise ValueError(f"CSV {src} is missing a header row.")
            for field in reader.fieldnames:
                if field not in columns:
                    columns.append(field)
            for row_idx, row in enumerate(reader):
                key_value = (row.get(key) or "").strip()
                if not key_value:
                    missing_key_count += 1
                    key_value = f"__missing__::{src.name}::{row_idx}"
                tag = src.stem
                if key_value not in merged_rows:
                    merged_rows[key_value] = {}
                    sources_by_key[key_value] = set()
                target_row = merged_rows[key_value]
                for col, val in row.items():
                    if col not in columns:
                        columns.append(col)
                    if val is None:
                        continue
                    val_str = str(val)
                    if target_row.get(col):
                        continue
                    target_row[col] = val_str
                sources_by_key[key_value].add(tag)

    if source_flag_col:
        if source_flag_col not in columns:
            columns.append(source_flag_col)

    output_path.parent.mkdir(parents=True, exist_ok=True)
    with output_path.open("w", newline="", encoding="utf-8") as handle:
        writer = csv.DictWriter(handle, fieldnames=columns)
        writer.writeheader()
        for key_value, row in merged_rows.items():
            output_row = {col: row.get(col, "") for col in columns}
            if source_flag_col:
                tags = sorted(list(sources_by_key.get(key_value, [])))
                output_row[source_flag_col] = ",".join(tags)
            writer.writerow(output_row)

    if missing_key_count:
        warnings.append(
            f"{missing_key_count} row(s) were missing key '{key}' and were retained with synthetic keys."
        )
    return MergeResult(rows_written=len(merged_rows), columns=columns, warnings=warnings)
