use super::Position;
use crate::board::zobrist::{Zobrist, ZobristKey};
use crate::types::{Color, Hand, HandPiece, Move, Move32, MoveBase, Piece};

#[allow(clippy::struct_field_names)]
pub(in crate::board) struct KeySet {
    pub(in crate::board) board_key: ZobristKey,
    pub(in crate::board) hand_key: ZobristKey,
}

impl Position {
    /// 盤上配置 + 手番のZobristキー（持ち駒は含まない）
    #[must_use]
    pub const fn board_key(&self) -> ZobristKey {
        self.board_key
    }

    /// 持ち駒のZobristキー
    #[must_use]
    pub const fn hand_key(&self) -> ZobristKey {
        self.hand_key
    }

    /// Zobristハッシュを完全計算
    pub(in crate::board) fn compute_keys(&self) -> KeySet {
        let mut board_key = ZobristKey::default();
        let mut hand_key = ZobristKey::default();

        // 盤上の駒
        for (sq, piece) in self.board.iter() {
            if !piece.is_empty() {
                board_key ^= Zobrist::psq(sq, piece);
            }
        }

        // 持ち駒
        for color in [Color::BLACK, Color::WHITE] {
            let hand = self.hand(color);
            for hp in 0..7 {
                let hp = HandPiece::new(hp);
                let count = Hand::count_of(hand, hp);
                if count > 0 {
                    hand_key.add(Zobrist::hand(color, hp.to_piece_type(), count as usize));
                }
            }
        }

        // 手番
        if self.turn() == Color::WHITE {
            board_key ^= Zobrist::side();
        }

        KeySet { board_key, hand_key }
    }

    /// 指し手適用後の `board_key`（盤上配置 + 手番）を計算
    #[must_use]
    pub fn board_key_after(&self, mv: Move32) -> ZobristKey {
        self.board_key_after_impl(mv)
    }

    /// `Move` 版の `board_key_after`。
    #[must_use]
    pub fn board_key_after_move(&self, mv: Move) -> ZobristKey {
        self.board_key_after_impl(mv)
    }

    fn board_key_after_impl(&self, mv: impl MoveBase) -> ZobristKey {
        let m = mv.to_move();
        let us = self.turn();
        let mut board_key = self.board_key ^ Zobrist::side();
        let to = m.to_sq();

        if m.is_drop() {
            let piece_type =
                m.dropped_piece().expect("drop move must include a dropped piece type");
            let piece = Piece::from_parts(us, piece_type);
            board_key ^= Zobrist::psq(to, piece);
            return board_key;
        }

        let from = m.from_sq();
        let moved_piece = self.piece_on(from);
        debug_assert!(moved_piece != Piece::NONE, "move must originate from a piece");
        let moved_after = if m.is_promotion() { moved_piece.promote() } else { moved_piece };

        let captured = self.piece_on(to);
        if captured != Piece::NONE {
            board_key ^= Zobrist::psq(to, captured);
        }

        board_key ^= Zobrist::psq(from, moved_piece);
        board_key ^= Zobrist::psq(to, moved_after);
        board_key
    }

    /// 指し手適用後の局面キー（`board_key ^ hand_key`）を計算
    #[must_use]
    pub fn key_after(&self, mv: Move32) -> ZobristKey {
        self.key_after_impl(mv)
    }

    /// `Move` 版の `key_after`。
    #[must_use]
    pub fn key_after_move(&self, mv: Move) -> ZobristKey {
        self.key_after_impl(mv)
    }

    fn key_after_impl(&self, mv: impl MoveBase) -> ZobristKey {
        let m = mv.to_move();
        let us = self.turn();
        let board_key = self.board_key_after_impl(mv);
        let mut hand_key = self.hand_key;
        let to = m.to_sq();

        if m.is_drop() {
            let piece_type =
                m.dropped_piece().expect("drop move must include a dropped piece type");
            hand_key.sub(Zobrist::hand(us, piece_type, 1));
        } else {
            let captured = self.piece_on(to);
            if captured != Piece::NONE {
                hand_key.add(Zobrist::hand(us, captured.piece_type().demote(), 1));
            }
        }

        board_key ^ hand_key
    }
}
