//! 棋譜データ構造
//!
//! このモジュールは棋譜を表現するための型を提供します。
//!
//! # 主要な型
//!
//! - [`GameRecord`] - 棋譜全体（本手順 + 分岐を木構造で保持）
//! - [`MoveRecord`] - 1 手分の記録（指し手・コメント・消費時間・エンジン解析情報）
//! - [`GameRecordMetadata`] - 対局メタデータ（対局者名・棋戦名・日時など）
//! - [`SpecialMoveRecord`] - 終局特殊手（投了・千日手・中断など）
//! - [`MoveEngineInfo`] - エンジン解析情報（評価値・探索深度・ノード数）
//! - [`MoveEngineExtraValue`] - エンジン拡張属性値（String / Int / Float / Bool）

use crate::records::error::RecordError;
use crate::records::time_control::TimeControl;
use crate::types::{Eval, GameResult, Move};
use ordered_float::OrderedFloat;
use std::collections::HashMap;

/// エンジン拡張属性の値。
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MoveEngineExtraValue {
    String(String),
    Int(i64),
    Float(OrderedFloat<f64>),
    Bool(bool),
}

impl From<String> for MoveEngineExtraValue {
    fn from(value: String) -> Self {
        Self::String(value)
    }
}

impl From<&str> for MoveEngineExtraValue {
    fn from(value: &str) -> Self {
        Self::String(value.to_string())
    }
}

impl From<i64> for MoveEngineExtraValue {
    fn from(value: i64) -> Self {
        Self::Int(value)
    }
}

impl From<i32> for MoveEngineExtraValue {
    fn from(value: i32) -> Self {
        Self::Int(i64::from(value))
    }
}

impl From<u32> for MoveEngineExtraValue {
    fn from(value: u32) -> Self {
        Self::Int(i64::from(value))
    }
}

impl From<f64> for MoveEngineExtraValue {
    fn from(value: f64) -> Self {
        Self::Float(OrderedFloat(value))
    }
}

impl From<f32> for MoveEngineExtraValue {
    fn from(value: f32) -> Self {
        Self::Float(OrderedFloat(f64::from(value)))
    }
}

impl From<bool> for MoveEngineExtraValue {
    fn from(value: bool) -> Self {
        Self::Bool(value)
    }
}

/// 1手分のエンジン解析情報。
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct MoveEngineInfo {
    eval: Option<Eval>,
    depth: Option<u16>,
    nodes: Option<u64>,
    seldepth: Option<u16>,
    extras: HashMap<String, MoveEngineExtraValue>,
}

impl MoveEngineInfo {
    /// 空のエンジン解析情報を構築します。
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// 評価値を設定します。
    #[must_use]
    pub fn with_eval(mut self, eval: Option<Eval>) -> Self {
        self.eval = eval;
        self
    }

    /// 深度を設定します。
    #[must_use]
    pub fn with_depth(mut self, depth: Option<u16>) -> Self {
        self.depth = depth;
        self
    }

    /// ノード数を設定します。
    #[must_use]
    pub fn with_nodes(mut self, nodes: Option<u64>) -> Self {
        self.nodes = nodes;
        self
    }

    /// 選択深度を設定します。
    #[must_use]
    pub fn with_seldepth(mut self, seldepth: Option<u16>) -> Self {
        self.seldepth = seldepth;
        self
    }

    /// 追加の解析属性を設定します。
    #[must_use]
    pub fn with_extra(mut self, key: String, value: MoveEngineExtraValue) -> Self {
        self.extras.insert(key, value);
        self
    }

    /// 評価値を取得します。
    #[must_use]
    pub const fn eval(&self) -> Option<Eval> {
        self.eval
    }

    /// 深度を取得します。
    #[must_use]
    pub const fn depth(&self) -> Option<u16> {
        self.depth
    }

    /// ノード数を取得します。
    #[must_use]
    pub const fn nodes(&self) -> Option<u64> {
        self.nodes
    }

    /// 選択深度を取得します。
    #[must_use]
    pub const fn seldepth(&self) -> Option<u16> {
        self.seldepth
    }

    /// 追加属性を取得します。
    #[must_use]
    pub const fn extras(&self) -> &HashMap<String, MoveEngineExtraValue> {
        &self.extras
    }

    /// 評価値を更新します。
    pub fn set_eval(&mut self, eval: Option<Eval>) {
        self.eval = eval;
    }

    /// 深度を更新します。
    pub fn set_depth(&mut self, depth: Option<u16>) {
        self.depth = depth;
    }

    /// ノード数を更新します。
    pub fn set_nodes(&mut self, nodes: Option<u64>) {
        self.nodes = nodes;
    }

    /// 選択深度を更新します。
    pub fn set_seldepth(&mut self, seldepth: Option<u16>) {
        self.seldepth = seldepth;
    }

    /// 追加属性を追加または更新します。
    pub fn set_extra(&mut self, key: String, value: MoveEngineExtraValue) {
        self.extras.insert(key, value);
    }

    /// 追加属性を削除します。
    #[must_use]
    pub fn remove_extra(&mut self, key: &str) -> Option<MoveEngineExtraValue> {
        self.extras.remove(key)
    }

    /// 追加属性を全削除します。
    pub fn clear_extras(&mut self) {
        self.extras.clear();
    }

    /// 解析情報が空かどうかを返します。
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.eval.is_none()
            && self.depth.is_none()
            && self.nodes.is_none()
            && self.seldepth.is_none()
            && self.extras.is_empty()
    }
}

/// 1手分の記録。
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MoveRecord {
    mv: Move,
    comment: Option<String>,
    time_ms: Option<u32>,
    engine_info: Option<MoveEngineInfo>,
}

impl MoveRecord {
    /// `Move` から構築します。
    #[must_use]
    pub const fn new(mv: Move) -> Self {
        Self { mv, comment: None, time_ms: None, engine_info: None }
    }

    /// コメントを設定します。
    #[must_use]
    pub fn with_comment(mut self, comment: Option<String>) -> Self {
        self.comment = comment;
        self
    }

    /// 消費時間(ミリ秒)を設定します。
    #[must_use]
    pub fn with_time_ms(mut self, time_ms: Option<u32>) -> Self {
        self.time_ms = time_ms;
        self
    }

    /// エンジン解析情報を設定します。
    #[must_use]
    pub fn with_engine_info(mut self, engine_info: Option<MoveEngineInfo>) -> Self {
        self.set_engine_info(engine_info);
        self
    }

    /// 評価値を設定します。
    #[must_use]
    pub fn with_eval(mut self, eval: Option<Eval>) -> Self {
        self.set_eval(eval);
        self
    }

    /// 深度を設定します。
    #[must_use]
    pub fn with_depth(mut self, depth: Option<u16>) -> Self {
        self.set_depth(depth);
        self
    }

    /// ノード数を設定します。
    #[must_use]
    pub fn with_nodes(mut self, nodes: Option<u64>) -> Self {
        self.set_nodes(nodes);
        self
    }

    /// 選択深度を設定します。
    #[must_use]
    pub fn with_seldepth(mut self, seldepth: Option<u16>) -> Self {
        self.set_seldepth(seldepth);
        self
    }

    /// 指し手を取得します。
    #[must_use]
    pub const fn mv(&self) -> Move {
        self.mv
    }

    /// コメントを取得します。
    #[must_use]
    pub fn comment(&self) -> Option<&str> {
        self.comment.as_deref()
    }

    /// 消費時間を取得します。
    #[must_use]
    pub const fn time_ms(&self) -> Option<u32> {
        self.time_ms
    }

    /// エンジン解析情報を取得します。
    #[must_use]
    pub const fn engine_info(&self) -> Option<&MoveEngineInfo> {
        self.engine_info.as_ref()
    }

    /// 評価値を取得します。
    #[must_use]
    pub fn eval(&self) -> Option<Eval> {
        self.engine_info().and_then(MoveEngineInfo::eval)
    }

    /// ノード数を取得します。
    #[must_use]
    pub fn nodes(&self) -> Option<u64> {
        self.engine_info().and_then(MoveEngineInfo::nodes)
    }

    /// 深度を取得します。
    #[must_use]
    pub fn depth(&self) -> Option<u16> {
        self.engine_info().and_then(MoveEngineInfo::depth)
    }

    /// 選択深度を取得します。
    #[must_use]
    pub fn seldepth(&self) -> Option<u16> {
        self.engine_info().and_then(MoveEngineInfo::seldepth)
    }

    /// コメントを更新します。
    pub fn set_comment(&mut self, comment: Option<String>) {
        self.comment = comment;
    }

    /// 消費時間を更新します。
    pub fn set_time_ms(&mut self, time_ms: Option<u32>) {
        self.time_ms = time_ms;
    }

    /// エンジン解析情報を更新します。
    pub fn set_engine_info(&mut self, engine_info: Option<MoveEngineInfo>) {
        self.engine_info = engine_info.filter(|info| !info.is_empty());
    }

    /// 評価値を更新します。
    pub fn set_eval(&mut self, eval: Option<Eval>) {
        self.ensure_engine_info().set_eval(eval);
        self.cleanup_engine_info_if_empty();
    }

    /// 深度を更新します。
    pub fn set_depth(&mut self, depth: Option<u16>) {
        self.ensure_engine_info().set_depth(depth);
        self.cleanup_engine_info_if_empty();
    }

    /// ノード数を更新します。
    pub fn set_nodes(&mut self, nodes: Option<u64>) {
        self.ensure_engine_info().set_nodes(nodes);
        self.cleanup_engine_info_if_empty();
    }

    /// 選択深度を更新します。
    pub fn set_seldepth(&mut self, seldepth: Option<u16>) {
        self.ensure_engine_info().set_seldepth(seldepth);
        self.cleanup_engine_info_if_empty();
    }

    fn ensure_engine_info(&mut self) -> &mut MoveEngineInfo {
        self.engine_info.get_or_insert_with(MoveEngineInfo::new)
    }

    fn cleanup_engine_info_if_empty(&mut self) {
        if let Some(info) = self.engine_info.as_ref() {
            if info.is_empty() {
                self.engine_info = None;
            }
        }
    }
}

/// 対局のメタ情報（棋戦名・対局者・持ち時間など）。
///
/// 各フィールドは `Option` で、情報が存在しない場合は `None` を返す。
/// [`GameRecordMetadataBuilder`] を使って構築できる。
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct GameRecordMetadata {
    event: Option<String>,
    site: Option<String>,
    black_player: Option<String>,
    white_player: Option<String>,
    game_name: Option<String>,
    game_type: Option<String>,
    time_control: Option<TimeControl>,
    black_time_control: Option<TimeControl>,
    white_time_control: Option<TimeControl>,
    max_moves: Option<u32>,
    impasse_rule: Option<String>,
    start_date: Option<String>,
    end_date: Option<String>,
    updated_date: Option<String>,
    comment: Option<String>,
    attributes: HashMap<String, String>,
}

impl GameRecordMetadata {
    /// 棋戦名を取得する。
    #[must_use]
    pub fn event(&self) -> Option<&str> {
        self.event.as_deref()
    }

    /// 対局場所を取得する。
    #[must_use]
    pub fn site(&self) -> Option<&str> {
        self.site.as_deref()
    }

    /// 先手の棋士名を取得する。
    #[must_use]
    pub fn black_player(&self) -> Option<&str> {
        self.black_player.as_deref()
    }

    /// 後手の棋士名を取得する。
    #[must_use]
    pub fn white_player(&self) -> Option<&str> {
        self.white_player.as_deref()
    }

    /// 対局名を取得する。
    #[must_use]
    pub fn game_name(&self) -> Option<&str> {
        self.game_name.as_deref()
    }

    /// 対局種別を取得する。
    #[must_use]
    pub fn game_type(&self) -> Option<&str> {
        self.game_type.as_deref()
    }

    /// 持ち時間設定を取得する。
    #[must_use]
    pub const fn time_control(&self) -> Option<&TimeControl> {
        self.time_control.as_ref()
    }

    /// 先手個別の持ち時間設定を取得する。
    #[must_use]
    pub const fn black_time_control(&self) -> Option<&TimeControl> {
        self.black_time_control.as_ref()
    }

    /// 後手個別の持ち時間設定を取得する。
    #[must_use]
    pub const fn white_time_control(&self) -> Option<&TimeControl> {
        self.white_time_control.as_ref()
    }

    /// 最大手数設定を取得する。
    #[must_use]
    pub const fn max_moves(&self) -> Option<u32> {
        self.max_moves
    }

    /// 持将棋（impasse）規定を取得する。
    #[must_use]
    pub fn impasse_rule(&self) -> Option<&str> {
        self.impasse_rule.as_deref()
    }

    /// 対局開始日を取得する。
    #[must_use]
    pub fn start_date(&self) -> Option<&str> {
        self.start_date.as_deref()
    }

    /// 対局終了日を取得する。
    #[must_use]
    pub fn end_date(&self) -> Option<&str> {
        self.end_date.as_deref()
    }

    /// 最終更新日時を取得する。
    #[must_use]
    pub fn updated_date(&self) -> Option<&str> {
        self.updated_date.as_deref()
    }

    /// コメントを取得する。
    #[must_use]
    pub fn comment(&self) -> Option<&str> {
        self.comment.as_deref()
    }

    /// フォーマット固有の追加属性を取得する。
    #[must_use]
    pub fn attributes(&self) -> &HashMap<String, String> {
        &self.attributes
    }

    /// [`GameRecordMetadataBuilder`] を生成する。
    #[must_use]
    pub fn builder() -> GameRecordMetadataBuilder {
        GameRecordMetadataBuilder::default()
    }
}

/// `GameRecordMetadata` を構築するためのビルダー。
#[derive(Default)]
pub struct GameRecordMetadataBuilder {
    event: Option<String>,
    site: Option<String>,
    black_player: Option<String>,
    white_player: Option<String>,
    game_name: Option<String>,
    game_type: Option<String>,
    time_control: Option<TimeControl>,
    black_time_control: Option<TimeControl>,
    white_time_control: Option<TimeControl>,
    max_moves: Option<u32>,
    impasse_rule: Option<String>,
    start_date: Option<String>,
    end_date: Option<String>,
    updated_date: Option<String>,
    comment: Option<String>,
    attributes: HashMap<String, String>,
}

impl GameRecordMetadataBuilder {
    /// 棋戦名を設定する。
    pub fn event(&mut self, value: Option<String>) -> &mut Self {
        self.event = value;
        self
    }

    /// 対局場所を設定する。
    pub fn site(&mut self, value: Option<String>) -> &mut Self {
        self.site = value;
        self
    }

    /// 先手の棋士名を設定する。
    pub fn black_player(&mut self, value: Option<String>) -> &mut Self {
        self.black_player = value;
        self
    }

    /// 後手の棋士名を設定する。
    pub fn white_player(&mut self, value: Option<String>) -> &mut Self {
        self.white_player = value;
        self
    }

    /// 対局名を設定する。
    pub fn game_name(&mut self, value: Option<String>) -> &mut Self {
        self.game_name = value;
        self
    }

    /// 対局種別を設定する。
    pub fn game_type(&mut self, value: Option<String>) -> &mut Self {
        self.game_type = value;
        self
    }

    /// 持ち時間設定を設定する。
    pub fn time_control(&mut self, value: Option<TimeControl>) -> &mut Self {
        self.time_control = value;
        self
    }

    /// 先手個別の持ち時間設定を設定する。
    pub fn black_time_control(&mut self, value: Option<TimeControl>) -> &mut Self {
        self.black_time_control = value;
        self
    }

    /// 後手個別の持ち時間設定を設定する。
    pub fn white_time_control(&mut self, value: Option<TimeControl>) -> &mut Self {
        self.white_time_control = value;
        self
    }

    /// 最大手数を設定する。
    pub fn max_moves(&mut self, value: Option<u32>) -> &mut Self {
        self.max_moves = value;
        self
    }

    /// 持将棋（impasse）規定を設定する。
    pub fn impasse_rule(&mut self, value: Option<String>) -> &mut Self {
        self.impasse_rule = value;
        self
    }

    /// 対局開始日を設定する。
    pub fn start_date(&mut self, value: Option<String>) -> &mut Self {
        self.start_date = value;
        self
    }

    /// 対局終了日を設定する。
    pub fn end_date(&mut self, value: Option<String>) -> &mut Self {
        self.end_date = value;
        self
    }

    /// 最終更新日時を設定する。
    pub fn updated_date(&mut self, value: Option<String>) -> &mut Self {
        self.updated_date = value;
        self
    }

    /// コメントを設定する。
    pub fn comment(&mut self, value: Option<String>) -> &mut Self {
        self.comment = value;
        self
    }

    /// フォーマット固有の追加属性を設定する。
    pub fn add_attribute(&mut self, key: String, value: String) -> &mut Self {
        self.attributes.insert(key, value);
        self
    }

    /// [`GameRecordMetadata`] を構築する。
    #[must_use]
    pub fn build(self) -> GameRecordMetadata {
        GameRecordMetadata {
            event: self.event,
            site: self.site,
            black_player: self.black_player,
            white_player: self.white_player,
            game_name: self.game_name,
            game_type: self.game_type,
            time_control: self.time_control,
            black_time_control: self.black_time_control,
            white_time_control: self.white_time_control,
            max_moves: self.max_moves,
            impasse_rule: self.impasse_rule,
            start_date: self.start_date,
            end_date: self.end_date,
            updated_date: self.updated_date,
            comment: self.comment,
            attributes: self.attributes,
        }
    }
}

/// 終局の特殊手種別。
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SpecialMove {
    Interrupt,
    Resign,
    MaxMoves,
    Impasse,
    Draw,
    RepetitionDraw,
    Mate,
    NoMate,
    Timeout,
    FoulWin,
    FoulLose,
    EnteringOfKing,
    WinByDefault,
    LoseByDefault,
    Try,
    Unknown(String),
}

/// 終局ノードの記録。
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SpecialMoveRecord {
    kind: SpecialMove,
    result: GameResult,
    comment: Option<String>,
    time_ms: Option<u32>,
    raw: Option<String>,
}

impl SpecialMoveRecord {
    #[must_use]
    pub const fn new(kind: SpecialMove, result: GameResult) -> Self {
        Self { kind, result, comment: None, time_ms: None, raw: None }
    }

    #[must_use]
    pub fn with_comment(mut self, comment: Option<String>) -> Self {
        self.comment = comment;
        self
    }

    #[must_use]
    pub fn with_time_ms(mut self, time_ms: Option<u32>) -> Self {
        self.time_ms = time_ms;
        self
    }

    #[must_use]
    pub fn with_raw(mut self, raw: Option<String>) -> Self {
        self.raw = raw;
        self
    }

    #[must_use]
    pub const fn kind(&self) -> &SpecialMove {
        &self.kind
    }

    #[must_use]
    pub const fn result(&self) -> GameResult {
        self.result
    }

    #[must_use]
    pub fn comment(&self) -> Option<&str> {
        self.comment.as_deref()
    }

    #[must_use]
    pub const fn time_ms(&self) -> Option<u32> {
        self.time_ms
    }

    #[must_use]
    pub fn raw(&self) -> Option<&str> {
        self.raw.as_deref()
    }

    pub fn set_comment(&mut self, comment: Option<String>) {
        self.comment = comment;
    }

    pub fn set_time_ms(&mut self, time_ms: Option<u32>) {
        self.time_ms = time_ms;
    }

    pub fn set_raw(&mut self, raw: Option<String>) {
        self.raw = raw;
    }
}

/// ノードが保持する記録。
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GameRecordEntry {
    Move(MoveRecord),
    Special(SpecialMoveRecord),
}

impl GameRecordEntry {
    #[must_use]
    pub fn as_move(&self) -> Option<&MoveRecord> {
        match self {
            Self::Move(mv) => Some(mv),
            Self::Special(_) => None,
        }
    }

    #[must_use]
    pub fn as_special(&self) -> Option<&SpecialMoveRecord> {
        match self {
            Self::Move(_) => None,
            Self::Special(special) => Some(special),
        }
    }
}

/// ノードID。
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NodeId(usize);

impl NodeId {
    /// 内部値（`nodes` 配列のインデックス）を取得
    #[inline]
    #[must_use]
    pub const fn raw(self) -> usize {
        self.0
    }

    /// 内部値から生成
    #[inline]
    #[must_use]
    pub const fn from_raw(raw: usize) -> Self {
        Self(raw)
    }
}

/// 棋譜ノード。
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GameRecordNode {
    parent: Option<NodeId>,
    variations: Vec<NodeId>,
    entry: Option<GameRecordEntry>,
}

impl GameRecordNode {
    fn root() -> Self {
        Self { parent: None, variations: Vec::new(), entry: None }
    }

    fn with_move(parent: NodeId, mv: MoveRecord) -> Self {
        Self {
            parent: Some(parent),
            variations: Vec::new(),
            entry: Some(GameRecordEntry::Move(mv)),
        }
    }

    fn with_special(parent: NodeId, special: SpecialMoveRecord) -> Self {
        Self {
            parent: Some(parent),
            variations: Vec::new(),
            entry: Some(GameRecordEntry::Special(special)),
        }
    }

    #[must_use]
    pub fn parent(&self) -> Option<NodeId> {
        self.parent
    }

    #[must_use]
    pub fn variations(&self) -> &[NodeId] {
        &self.variations
    }

    #[must_use]
    pub fn entry(&self) -> Option<&GameRecordEntry> {
        self.entry.as_ref()
    }

    #[must_use]
    pub fn mv(&self) -> Option<&MoveRecord> {
        self.entry().and_then(GameRecordEntry::as_move)
    }

    #[must_use]
    pub fn special(&self) -> Option<&SpecialMoveRecord> {
        self.entry().and_then(GameRecordEntry::as_special)
    }

    fn set_special(&mut self, special: SpecialMoveRecord) {
        self.entry = Some(GameRecordEntry::Special(special));
    }
}

/// 棋譜全体を表すツリー構造。
///
/// 初期局面 SFEN・メタ情報・終局情報・手順ツリーを保持する。
/// 本手順は先頭の子ノード、変化手順はそれ以降の子ノードとして管理される。
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GameRecord {
    init_position_sfen: String,
    metadata: GameRecordMetadata,
    root: NodeId,
    nodes: Vec<GameRecordNode>,
}

impl GameRecord {
    /// 空の棋譜（手順なし）を構築します。
    pub fn new(init_position_sfen: String) -> Result<Self, RecordError> {
        if init_position_sfen.is_empty() {
            return Err(RecordError::EmptyInitPosition);
        }

        let root = NodeId::from_raw(0);
        Ok(GameRecord {
            init_position_sfen,
            metadata: GameRecordMetadata::default(),
            root,
            nodes: vec![GameRecordNode::root()],
        })
    }

    /// 本手順のみから棋譜を構築します。
    ///
    /// `result` は本手順末尾の終局特殊手として保持されます。
    pub fn from_main_line_with_result(
        init_position_sfen: String,
        moves: Vec<MoveRecord>,
        result: GameResult,
    ) -> Result<Self, RecordError> {
        let mut record = Self::new(init_position_sfen)?;
        record.extend_main_line(moves);
        let special = SpecialMoveRecord::new(special_from_game_result(result), result);
        let _ = record.set_main_terminal(special);
        Ok(record)
    }

    /// 本手順 + 終局特殊手から棋譜を構築します。
    pub fn from_main_line(
        init_position_sfen: String,
        moves: Vec<MoveRecord>,
        terminal: Option<SpecialMoveRecord>,
    ) -> Result<Self, RecordError> {
        let mut record = Self::new(init_position_sfen)?;
        record.extend_main_line(moves);
        if let Some(terminal) = terminal {
            let _ = record.set_main_terminal(terminal);
        }
        Ok(record)
    }

    /// 指し手数（半手）
    #[must_use]
    pub fn move_count(&self) -> usize {
        self.main_moves().count()
    }

    /// 初期局面 SFEN を取得します。
    #[must_use]
    pub fn init_position_sfen(&self) -> &str {
        &self.init_position_sfen
    }

    /// メタ情報を取得します。
    #[must_use]
    pub const fn metadata(&self) -> &GameRecordMetadata {
        &self.metadata
    }

    /// メタ情報を設定します。
    pub fn set_metadata(&mut self, metadata: GameRecordMetadata) {
        self.metadata = metadata;
    }

    /// 終局結果を取得します。
    ///
    /// 本手順末尾に終局特殊手が存在しない場合は [`GameResult::Invalid`] を返します。
    #[must_use]
    pub fn result(&self) -> GameResult {
        self.main_terminal().map_or(GameResult::Invalid, SpecialMoveRecord::result)
    }

    /// 本手順の終局特殊手を取得します。
    #[must_use]
    pub fn main_terminal(&self) -> Option<&SpecialMoveRecord> {
        let tail = self.main_line_tail();
        if tail == self.root {
            return None;
        }
        self.node(tail).special()
    }

    /// 本手順の終局特殊手を設定します。
    pub fn set_main_terminal(&mut self, special: SpecialMoveRecord) -> NodeId {
        let tail = self.main_line_tail();
        if tail != self.root && self.node(tail).special().is_some() {
            self.nodes[tail.raw()].set_special(special);
            return tail;
        }
        self.add_main_special(tail, special)
    }

    /// ルートノードIDを取得します。
    #[must_use]
    pub const fn root_id(&self) -> NodeId {
        self.root
    }

    /// ノード数を取得します。
    #[must_use]
    pub fn node_count(&self) -> usize {
        self.nodes.len()
    }

    /// ノードを取得します。
    #[must_use]
    pub fn node(&self, node_id: NodeId) -> &GameRecordNode {
        &self.nodes[node_id.raw()]
    }

    /// 子ノード一覧を取得します（先頭が本手順）。
    #[must_use]
    pub fn children(&self, node_id: NodeId) -> &[NodeId] {
        &self.nodes[node_id.raw()].variations
    }

    /// 本手順の指し手をイテレートします。
    #[must_use]
    pub fn main_moves(&self) -> MainLineMoves<'_> {
        MainLineMoves { record: self, current: self.main_child(self.root) }
    }

    /// 本手順の指し手をVecで取得します。
    #[must_use]
    pub fn moves(&self) -> Vec<MoveRecord> {
        self.main_moves().cloned().collect()
    }

    /// 本手順のノードID一覧を取得します。
    #[must_use]
    pub fn main_line_ids(&self) -> Vec<NodeId> {
        let mut ids = Vec::new();
        let mut current = self.main_child(self.root);
        while let Some(node_id) = current {
            if self.node(node_id).mv().is_some() {
                ids.push(node_id);
            }
            current = self.main_child(node_id);
        }
        ids
    }

    /// 本手順の末尾ノードを取得します（存在しない場合はルート）。
    #[must_use]
    pub fn main_line_tail(&self) -> NodeId {
        let mut current = self.root;
        while let Some(next) = self.main_child(current) {
            current = next;
        }
        current
    }

    /// 本手順を末尾に追加します。
    pub fn extend_main_line(&mut self, moves: Vec<MoveRecord>) -> Vec<NodeId> {
        let mut created = Vec::new();
        let mut current = self.main_line_tail();
        for mv in moves {
            let child = self.add_main_child(current, mv);
            created.push(child);
            current = child;
        }
        created
    }

    /// 変化手順を指定ノードに追加します（本手順以外）。
    pub fn add_variation_line(&mut self, parent: NodeId, moves: Vec<MoveRecord>) -> Vec<NodeId> {
        if moves.is_empty() {
            return Vec::new();
        }
        let mut created = Vec::new();
        let first = self.push_node(GameRecordNode::with_move(parent, moves[0].clone()));
        self.nodes[parent.raw()].variations.push(first);
        created.push(first);
        let mut current = first;
        for mv in moves.into_iter().skip(1) {
            let child = self.add_main_child(current, mv);
            created.push(child);
            current = child;
        }
        created
    }

    fn main_child(&self, node_id: NodeId) -> Option<NodeId> {
        self.nodes[node_id.raw()].variations.first().copied()
    }

    fn add_main_child(&mut self, parent: NodeId, mv: MoveRecord) -> NodeId {
        let child = self.push_node(GameRecordNode::with_move(parent, mv));
        if self.nodes[parent.raw()].variations.is_empty() {
            self.nodes[parent.raw()].variations.push(child);
        } else {
            self.nodes[parent.raw()].variations.insert(0, child);
        }
        child
    }

    fn add_main_special(&mut self, parent: NodeId, special: SpecialMoveRecord) -> NodeId {
        let child = self.push_node(GameRecordNode::with_special(parent, special));
        if self.nodes[parent.raw()].variations.is_empty() {
            self.nodes[parent.raw()].variations.push(child);
        } else {
            self.nodes[parent.raw()].variations.insert(0, child);
        }
        child
    }

    fn push_node(&mut self, node: GameRecordNode) -> NodeId {
        let id = NodeId::from_raw(self.nodes.len());
        self.nodes.push(node);
        id
    }
}

/// 本手順の [`MoveRecord`] を順に返すイテレータ。
pub struct MainLineMoves<'a> {
    record: &'a GameRecord,
    current: Option<NodeId>,
}

impl<'a> Iterator for MainLineMoves<'a> {
    type Item = &'a MoveRecord;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(current_id) = self.current {
            let node = self.record.node(current_id);
            self.current = self.record.main_child(current_id);
            if let Some(mv) = node.mv() {
                return Some(mv);
            }
        }
        None
    }
}

fn special_from_game_result(result: GameResult) -> SpecialMove {
    match result {
        GameResult::BlackWin | GameResult::WhiteWin => SpecialMove::Resign,
        GameResult::DrawByRepetition => SpecialMove::RepetitionDraw,
        GameResult::Error => SpecialMove::Unknown("ERROR".to_string()),
        GameResult::BlackWinByDeclaration | GameResult::WhiteWinByDeclaration => {
            SpecialMove::EnteringOfKing
        }
        GameResult::DrawByMaxPlies => SpecialMove::MaxMoves,
        GameResult::Invalid => SpecialMove::Unknown("INVALID".to_string()),
        GameResult::BlackWinByForfeit | GameResult::WhiteWinByForfeit => SpecialMove::WinByDefault,
        GameResult::DrawByImpasse => SpecialMove::Impasse,
        GameResult::Paused => SpecialMove::Interrupt,
        GameResult::BlackWinByIllegalMove | GameResult::WhiteWinByIllegalMove => {
            SpecialMove::FoulLose
        }
        GameResult::BlackWinByTimeout | GameResult::WhiteWinByTimeout => SpecialMove::Timeout,
    }
}
