#!/usr/bin/env python3
"""真实采集会话索引写入入口。"""

from __future__ import annotations

import argparse
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

import yaml


PROJECT_ROOT = Path(__file__).resolve().parents[3]
DEPLOYMENT_TOOLS = PROJECT_ROOT / "src" / "deployment" / "tools"
if str(DEPLOYMENT_TOOLS) not in sys.path:
    sys.path.insert(0, str(DEPLOYMENT_TOOLS))

from dataset_index_to_site_data_input import SERVICE_INPUT_FIELDS, load_yaml
from finalize_site_session import (
    load_site_profile,
    resolve_dataset_index,
    resolve_output_path,
    resolve_session_dir,
)


def now_us() -> int:
    return int(datetime.now(timezone.utc).timestamp() * 1_000_000)


def default_session_id() -> str:
    return datetime.now().strftime("session_%Y%m%d_%H%M%S")


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="把真实采集产物登记到 dataset_index.yaml，并可选生成 site_data_input.yaml。"
    )
    parser.add_argument(
        "--site-profile",
        default="src/deployment/profiles/site_template.yaml",
        help="现场部署 profile 路径。",
    )
    parser.add_argument(
        "--session-id",
        default="",
        help="本次标定会话 ID；不填写时自动生成。",
    )
    parser.add_argument(
        "--session-dir",
        help="本次标定会话目录；优先级高于 --session-id。",
    )
    parser.add_argument(
        "--site-id",
        default="",
        help="现场或产线 ID。",
    )
    parser.add_argument(
        "--vehicle-id",
        default="",
        help="车辆 ID；默认读取 site profile 的 vehicle_id。",
    )
    parser.add_argument(
        "--start-us",
        type=int,
        default=0,
        help="采集窗口开始时间，微秒 Unix 时间戳；不填写时使用当前时间。",
    )
    parser.add_argument(
        "--end-us",
        type=int,
        default=0,
        help="采集窗口结束时间，微秒 Unix 时间戳；--finalize 时不填写则使用当前时间。",
    )
    parser.add_argument(
        "--file",
        action="append",
        default=[],
        metavar="模块.字段=路径",
        help="登记采集文件，可重复，例如 sensor.image_sample_files=sensor/front_camera/images.yaml。",
    )
    parser.add_argument(
        "--overwrite",
        action="store_true",
        help="覆盖已有 dataset_index.yaml；默认合并已有索引。",
    )
    parser.add_argument(
        "--finalize",
        action="store_true",
        help="写完 dataset_index.yaml 后立即生成 site_data_input.yaml。",
    )
    parser.add_argument(
        "--site-data-output",
        help="生成的 site_data_input.yaml 路径；仅 --finalize 时生效。",
    )
    parser.add_argument(
        "--no-strict",
        action="store_true",
        help="生成 site_data_input.yaml 时关闭严格检查。",
    )
    return parser.parse_args()


def known_input_fields() -> set[str]:
    result: set[str] = set()
    for config in SERVICE_INPUT_FIELDS.values():
        section_name = str(config["index_section"])
        for field_name in config["fields"]:
            result.add(f"{section_name}.{field_name}")
    return result


def parse_file_reference(raw_value: str, allowed_fields: set[str]) -> tuple[str, str, str]:
    if "=" not in raw_value:
        raise ValueError(f"--file 必须使用 模块.字段=路径 格式，当前值为 {raw_value!r}。")
    key, path = raw_value.split("=", 1)
    key = key.strip()
    path = path.strip()
    if "." not in key:
        raise ValueError(f"--file 左侧必须是 模块.字段，当前值为 {key!r}。")
    section_name, field_name = key.split(".", 1)
    if key not in allowed_fields:
        raise ValueError(f"未知数据输入字段 {key!r}。")
    if not path:
        raise ValueError(f"{key} 的路径不能为空。")
    return section_name, field_name, path


def empty_data_inputs() -> dict[str, Any]:
    return {
        str(config["index_section"]): {}
        for config in SERVICE_INPUT_FIELDS.values()
    }


def merge_file_reference(index: dict[str, Any], section_name: str, field_name: str, path: str) -> None:
    data_inputs = index.setdefault("data_inputs", empty_data_inputs())
    section = data_inputs.setdefault(section_name, {})
    files = section.setdefault(field_name, [])
    if isinstance(files, str):
        files = [files]
        section[field_name] = files
    if not isinstance(files, list):
        raise ValueError(f"data_inputs.{section_name}.{field_name} 必须是列表或字符串。")
    if path not in files:
        files.append(path)


def make_base_index(
    session_id: str,
    site_id: str,
    vehicle_id: str,
    session_dir: Path,
    start_us: int,
    end_us: int,
) -> dict[str, Any]:
    return {
        "schema_version": 1,
        "session": {
            "session_id": session_id,
            "site_id": site_id,
            "vehicle_id": vehicle_id,
            "dataset_root": str(session_dir),
            "data_window_start_timestamp_us": start_us,
            "data_window_end_timestamp_us": end_us,
        },
        "data_inputs": empty_data_inputs(),
    }


def update_session_metadata(
    index: dict[str, Any],
    session_id: str,
    site_id: str,
    vehicle_id: str,
    session_dir: Path,
    start_us: int,
    end_us: int,
) -> None:
    session = index.setdefault("session", {})
    if not isinstance(session, dict):
        raise ValueError("dataset_index.yaml 中的 session 必须是 YAML map。")
    session["session_id"] = session_id
    session["site_id"] = site_id
    session["vehicle_id"] = vehicle_id
    session["dataset_root"] = str(session_dir)
    session["data_window_start_timestamp_us"] = start_us
    session["data_window_end_timestamp_us"] = end_us
    index.setdefault("schema_version", 1)
    index.setdefault("data_inputs", empty_data_inputs())


def write_yaml(path: Path, data: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(
        yaml.safe_dump(data, sort_keys=False, allow_unicode=True),
        encoding="utf-8",
    )


def main() -> int:
    args = parse_args()
    profile_path = Path(args.site_profile).expanduser().resolve(strict=False)
    profile = load_site_profile(profile_path)
    session_id = args.session_id.strip() or default_session_id()
    session_dir = resolve_session_dir(profile, profile_path, session_id, args.session_dir)
    dataset_index_path = resolve_dataset_index(profile, profile_path, session_dir, None)
    site_id = args.site_id.strip()
    vehicle_id = args.vehicle_id.strip() or str(profile.get("vehicle_id", "")).strip()
    start_us = args.start_us or now_us()
    end_us = args.end_us
    if args.finalize and end_us <= 0:
        end_us = now_us()
    if end_us > 0 and end_us <= start_us:
        end_us = start_us + 1

    if dataset_index_path.exists() and not args.overwrite:
        index = load_yaml(dataset_index_path)
    else:
        index = make_base_index(session_id, site_id, vehicle_id, session_dir, start_us, end_us)

    update_session_metadata(index, session_id, site_id, vehicle_id, session_dir, start_us, end_us)

    allowed_fields = known_input_fields()
    for raw_file in args.file:
        section_name, field_name, file_path = parse_file_reference(raw_file, allowed_fields)
        merge_file_reference(index, section_name, field_name, file_path)

    write_yaml(dataset_index_path, index)
    print(f"已写入数据集索引：{dataset_index_path}")

    if args.finalize:
        from dataset_index_to_site_data_input import build_ros_params, validate_output

        output_path = resolve_output_path(
            profile,
            profile_path,
            session_dir,
            session_id,
            args.site_data_output,
        )
        output = build_ros_params(
            dataset_index_path,
            index,
            str(session_dir),
            True,
        )
        validate_output(output, strict=not args.no_strict)
        write_yaml(output_path, output)
        print(f"已生成数据输入参数文件：{output_path}")
        print(f"启动参数：data_input_params_file:={output_path}")

    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except Exception as exc:
        print(f"[错误] {exc}", file=sys.stderr)
        raise SystemExit(1)
