"""Checkpoint 해커톤 제출 파일 생성 헬퍼.

이 함수는 점수를 계산하지 않습니다. 학생 모델의 예측값을 표준 CSV로
저장하고, 재현성 확인용 메타데이터를 result.json에 기록합니다.
실제 순위 점수는 Checkpoint 서버가 비공개 정답키로 다시 계산합니다.
"""

from __future__ import annotations

import csv
import hashlib
import json
import math
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Iterable, Mapping, Sequence


def _is_empty(value: Any) -> bool:
    if value is None:
        return True
    if isinstance(value, float) and math.isnan(value):
        return True
    return str(value).strip() == ""


def save_checkpoint_result(
    ids: Iterable[Any],
    predictions: Iterable[Any],
    *,
    model_name: str,
    seed: int,
    experiments: Sequence[Mapping[str, Any]] | None = None,
    output_dir: str | Path = ".",
    id_column: str | None = None,
    target_column: str | None = None,
) -> tuple[Path, Path]:
    """submission.csv와 result.json을 생성한다.

    Args:
        ids: 평가 데이터의 ID. 중복과 빈 값은 허용하지 않는다.
        predictions: 각 ID에 대응하는 예측 label.
        model_name: 이번 제출 모델을 구분하는 이름.
        seed: 재현에 사용한 random seed.
        experiments: baseline과 개선 실험 요약. 순위 점수에는 사용하지 않는다.
        output_dir: 두 결과 파일을 저장할 폴더.
        id_column: 과제에 표시된 제출 CSV의 ID 열 이름. 반드시 지정한다.
        target_column: 과제에 표시된 예측 열 이름. 반드시 지정한다.
    """

    id_values = list(ids)
    prediction_values = list(predictions)

    if not model_name.strip():
        raise ValueError("model_name을 입력하세요.")
    if not id_column or not target_column:
        raise ValueError(
            "id_column과 target_column을 과제 규격에 맞게 지정하세요. "
            '예: dl-w1은 id_column="ID", target_column="label"'
        )
    if not id_column.strip() or not target_column.strip():
        raise ValueError("열 이름은 비워둘 수 없습니다.")
    if len(id_values) != len(prediction_values):
        raise ValueError("ID 개수와 예측값 개수가 다릅니다.")
    if not id_values:
        raise ValueError("최소 1개 이상의 예측값이 필요합니다.")
    if any(_is_empty(value) for value in id_values):
        raise ValueError("ID에 빈 값이 있습니다.")
    if any(_is_empty(value) for value in prediction_values):
        raise ValueError("예측값에 빈 값 또는 NaN이 있습니다.")

    normalized_ids = [str(value).strip() for value in id_values]
    if len(set(normalized_ids)) != len(normalized_ids):
        raise ValueError("중복 ID가 있습니다.")

    destination = Path(output_dir)
    destination.mkdir(parents=True, exist_ok=True)
    submission_path = destination / "submission.csv"
    result_path = destination / "result.json"

    with submission_path.open("w", newline="", encoding="utf-8") as stream:
        writer = csv.writer(stream)
        writer.writerow([id_column, target_column])
        writer.writerows(zip(id_values, prediction_values, strict=True))

    prediction_sha256 = hashlib.sha256(submission_path.read_bytes()).hexdigest()
    result = {
        "contract_version": "checkpoint-hackathon/v1",
        "model_name": model_name.strip(),
        "seed": seed,
        "rows": len(id_values),
        "prediction_sha256": prediction_sha256,
        "experiments": list(experiments or []),
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "notice": "점수와 순위는 Checkpoint 서버가 submission.csv를 재채점해 결정합니다.",
    }
    result_path.write_text(
        json.dumps(result, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )

    print(f"submission: {submission_path} ({len(id_values)} rows)")
    print(f"metadata:   {result_path}")
    return submission_path, result_path
