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

/// 1手分の記録。
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MoveRecord {
    mv: Move,
    eval: Option<Eval>,
    comment: Option<String>,
    time_ms: Option<u32>,
    nodes: Option<u64>,
    depth: Option<u16>,
    seldepth: Option<u16>,
}

impl MoveRecord {
    /// `Move` と任意の評価値から構築します。
    #[must_use]
    pub fn new(mv: Move, eval: Option<Eval>) -> Self {
        Self { mv, eval, comment: None, time_ms: None, nodes: None, depth: None, seldepth: 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_nodes(mut self, nodes: Option<u64>) -> Self {
        self.nodes = nodes;
        self
    }

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

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

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

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

    /// コメントを取得します。
    #[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 nodes(&self) -> Option<u64> {
        self.nodes
    }

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

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

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

    /// コメントを更新します。
    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;
    }
}

/// 対局のメタ情報。
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct GameRecordMetadata {
    event: Option<String>,
    site: Option<String>,
    black_player: Option<String>,
    white_player: Option<String>,
    time_control: Option<TimeControl>,
    start_date: Option<String>,
    end_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 const fn time_control(&self) -> Option<&TimeControl> {
        self.time_control.as_ref()
    }

    #[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 comment(&self) -> Option<&str> {
        self.comment.as_deref()
    }

    #[must_use]
    pub fn attributes(&self) -> &HashMap<String, String> {
        &self.attributes
    }

    #[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>,
    time_control: Option<TimeControl>,
    start_date: Option<String>,
    end_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 time_control(&mut self, value: Option<TimeControl>) -> &mut Self {
        self.time_control = 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 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
    }

    #[must_use]
    pub fn build(self) -> GameRecordMetadata {
        GameRecordMetadata {
            event: self.event,
            site: self.site,
            black_player: self.black_player,
            white_player: self.white_player,
            time_control: self.time_control,
            start_date: self.start_date,
            end_date: self.end_date,
            comment: self.comment,
            attributes: self.attributes,
        }
    }
}

/// 終局情報。
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GameRecordResult {
    result: GameResult,
}

impl GameRecordResult {
    #[must_use]
    pub const fn new(result: GameResult) -> Self {
        Self { result }
    }

    /// 結果コードを取得します。
    #[must_use]
    pub const fn result(&self) -> GameResult {
        self.result
    }
}

/// ノード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>,
    mv: Option<MoveRecord>,
}

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

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

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

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

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

/// 棋譜全体。
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GameRecord {
    init_position_sfen: String,
    result: GameRecordResult,
    metadata: GameRecordMetadata,
    root: NodeId,
    nodes: Vec<GameRecordNode>,
}

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

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

    /// 本手順のみから棋譜を構築します。
    pub fn from_main_line(
        init_position_sfen: String,
        moves: Vec<MoveRecord>,
        result: GameRecordResult,
    ) -> Result<Self, RecordError> {
        let mut record = Self::new(init_position_sfen, result)?;
        record.extend_main_line(moves);
        Ok(record)
    }

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

    /// 初期局面 SFEN を取得します。
    #[must_use]
    pub fn init_position_to_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;
    }

    /// 終局情報を取得します。
    #[must_use]
    pub const fn result(&self) -> &GameRecordResult {
        &self.result
    }

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

    /// ノード数を取得します。
    #[must_use]
    pub fn node_len(&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 {
            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 push_node(&mut self, node: GameRecordNode) -> NodeId {
        let id = NodeId::from_raw(self.nodes.len());
        self.nodes.push(node);
        id
    }
}

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> {
        let current_id = self.current?;
        let node = self.record.node(current_id);
        self.current = self.record.main_child(current_id);
        node.mv.as_ref()
    }
}
