from __future__ import annotations

import argparse
import json
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List

from .decoder import decode_base64_image, detect_image_extension, write_image_bytes
from .manifest import build_manifest_payload, write_json
from .naming import build_image_filename, build_output_dir_name
from .scanner import EventInfoRecord, scan_event_info_dir, summarize_event_types


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description="EVENT_INFO JSON들의 event_image(base64)를 이미지 파일로 복원합니다."
    )
    parser.add_argument("--input-dir", required=True, help="EVENT_INFO_*.json 파일들이 있는 폴더")
    parser.add_argument(
        "--output-root",
        default="/data/offline_runs_base64_convert_test/output",
        help="변환 결과를 저장할 output 루트 폴더",
    )
    parser.add_argument(
        "--output-name",
        default="",
        help="자동 생성 대신 사용할 output 하위폴더 이름",
    )
    parser.add_argument(
        "--limit",
        type=int,
        default=0,
        help="앞에서부터 N개 파일만 변환합니다. 0이면 전체",
    )
    parser.add_argument(
        "--overwrite",
        action="store_true",
        help="동일 파일이 이미 있으면 덮어씁니다.",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="파일 저장 없이 어떤 파일을 변환할지만 출력합니다.",
    )
    return parser


def _append_error(errors_path: Path, payload: Dict[str, Any]) -> None:
    errors_path.parent.mkdir(parents=True, exist_ok=True)
    with errors_path.open("a", encoding="utf-8") as fp:
        fp.write(json.dumps(payload, ensure_ascii=False) + "\n")


def _select_records(records: List[EventInfoRecord], limit: int) -> List[EventInfoRecord]:
    if limit and limit > 0:
        return records[:limit]
    return records


def run(args: argparse.Namespace) -> int:
    input_dir = Path(args.input_dir).expanduser().resolve()
    output_root = Path(args.output_root).expanduser().resolve()
    records = _select_records(scan_event_info_dir(input_dir), args.limit)

    first_cctv_no = records[0].cctv_no if records else "na"
    output_dir_name = build_output_dir_name(
        input_dir=input_dir,
        cctv_no=first_cctv_no,
        output_name=str(args.output_name or "").strip() or None,
    )
    output_dir = output_root / output_dir_name
    images_dir = output_dir / "images"
    errors_path = output_dir / "errors.jsonl"
    manifest_path = output_dir / "manifest.json"

    started_at = datetime.now().isoformat()
    decoded_files = 0
    skipped_files = 0
    failed_files = 0

    for record in records:
        if not record.has_base64:
            skipped_files += 1
            _append_error(
                errors_path,
                {
                    "file": str(record.path),
                    "event_no": record.event_no,
                    "status": "skipped",
                    "reason": "event_image missing",
                },
            )
            continue

        try:
            image_bytes = decode_base64_image(str(record.payload.get("event_image") or ""))
            extension = detect_image_extension(image_bytes)
            filename = build_image_filename(
                record.event_no,
                record.event_type,
                record.event_timestamp,
                extension,
            )
            target_path = images_dir / filename
            if args.dry_run:
                print(f"[dry-run] {record.path.name} -> {target_path}")
                decoded_files += 1
                continue
            write_image_bytes(target_path, image_bytes, overwrite=bool(args.overwrite))
            decoded_files += 1
        except Exception as exc:
            failed_files += 1
            _append_error(
                errors_path,
                {
                    "file": str(record.path),
                    "event_no": record.event_no,
                    "status": "failed",
                    "reason": str(exc),
                },
            )

    finished_at = datetime.now().isoformat()
    manifest_payload = build_manifest_payload(
        input_dir=str(input_dir),
        output_dir=str(output_dir),
        started_at=started_at,
        finished_at=finished_at,
        total_files=len(records),
        decoded_files=decoded_files,
        skipped_files=skipped_files,
        failed_files=failed_files,
        event_type_counts=summarize_event_types(records),
        errors_path=str(errors_path),
    )
    if not args.dry_run:
        write_json(manifest_path, manifest_payload)

    print(
        json.dumps(
            {
                "input_dir": str(input_dir),
                "output_dir": str(output_dir),
                "total_files": len(records),
                "decoded_files": decoded_files,
                "skipped_files": skipped_files,
                "failed_files": failed_files,
                "dry_run": bool(args.dry_run),
            },
            ensure_ascii=False,
        )
    )
    return 0 if failed_files == 0 else 1


def main() -> int:
    parser = build_parser()
    args = parser.parse_args()
    return run(args)


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