use super::Position;
use crate::types::{Bitboard, Color, HandPiece, Move, Move16, Piece, PieceType, Rank};

impl Position {
    /// 指し手がpseudo-legalかどうかを判定する（自殺手チェックは含まない）。
    #[must_use]
    #[allow(clippy::too_many_lines)]
    pub fn is_pseudo_legal(&self, mv: Move, generate_all_legal_moves: bool) -> bool {
        let us = self.turn();
        let to = mv.to_sq();

        if mv.is_drop() {
            let Some(piece_type) = mv.dropped_piece() else {
                return false;
            };
            let moved_after = mv.moved_after_piece();
            if moved_after != Piece::NONE && moved_after != Piece::make(us, piece_type) {
                return false;
            }
            let hand_piece = HandPiece::from_piece_type(piece_type)
                .expect("dropped piece must map to hand piece");
            if self.piece_on(to) != Piece::NONE {
                return false;
            }
            if self.hand(us).count(hand_piece) == 0 {
                return false;
            }

            let checkers = self.checkers();
            if !checkers.is_empty() {
                let mut checkers_bb = checkers;
                let checker_sq = checkers_bb.pop_lsb().expect("checker exists");
                if !checkers_bb.is_empty() {
                    return false;
                }
                let king_sq = self.king_square(us);
                if !Bitboard::between(checker_sq, king_sq).test(to) {
                    return false;
                }
            }

            if piece_type == PieceType::PAWN && !self.legal_pawn_drop(us, to) {
                return false;
            }

            return true;
        }

        let from = mv.from_sq();
        let piece = self.piece_on(from);
        if piece == Piece::NONE || piece.color() != us {
            return false;
        }
        let moved_after = mv.moved_after_piece();
        if moved_after != Piece::NONE {
            let expected = if mv.is_promote() { piece.promote() } else { piece };
            if moved_after != expected {
                return false;
            }
        }

        let occupied = self.bitboards().occupied();
        if !Self::piece_attacks(piece.piece_type(), from, us, occupied).test(to) {
            return false;
        }

        if self.bitboards().color_pieces(us).test(to) {
            return false;
        }
        let pt = piece.piece_type();
        if mv.is_promote() {
            if !pt.is_promotable() {
                return false;
            }
        } else {
            if pt == PieceType::NONE {
                return false;
            }
            if generate_all_legal_moves {
                if (pt == PieceType::PAWN || pt == PieceType::LANCE)
                    && ((us == Color::BLACK && to.rank() == Rank::RANK_1)
                        || (us == Color::WHITE && to.rank() == Rank::RANK_9))
                {
                    return false;
                }
            } else {
                match pt {
                    PieceType::PAWN => {
                        let enemy_territory = match us {
                            Color::BLACK => {
                                Bitboard::rank_mask(Rank::RANK_1)
                                    | Bitboard::rank_mask(Rank::RANK_2)
                                    | Bitboard::rank_mask(Rank::RANK_3)
                            }
                            Color::WHITE => {
                                Bitboard::rank_mask(Rank::RANK_7)
                                    | Bitboard::rank_mask(Rank::RANK_8)
                                    | Bitboard::rank_mask(Rank::RANK_9)
                            }
                        };
                        if enemy_territory.test(to) {
                            return false;
                        }
                    }
                    PieceType::LANCE => {
                        if (us == Color::BLACK && to.rank() <= Rank::RANK_2)
                            || (us == Color::WHITE && to.rank() >= Rank::RANK_8)
                        {
                            return false;
                        }
                    }
                    PieceType::BISHOP | PieceType::ROOK => {
                        if Self::can_promote(from, to, us) {
                            return false;
                        }
                    }
                    _ => {}
                }
            }
        }

        let checkers = self.checkers();
        if !checkers.is_empty() && pt != PieceType::KING {
            if checkers.count() > 1 {
                return false;
            }
            let king_sq = self.king_square(us);
            let checker_sq = checkers.lsb().expect("single checker exists");
            let target =
                Bitboard::between(checker_sq, king_sq).or(Bitboard::from_square(checker_sq));
            if !target.test(to) {
                return false;
            }
        }

        true
    }

    /// `Move16` 版の pseudo-legal 判定。
    #[must_use]
    #[inline]
    pub fn is_pseudo_legal_move16(&self, mv: Move16, generate_all_legal_moves: bool) -> bool {
        self.is_pseudo_legal(Move::from_move16(mv), generate_all_legal_moves)
    }

    /// `Move16` 版の legal 判定。
    #[must_use]
    #[inline]
    pub fn is_legal_move16(&self, mv: Move16) -> bool {
        self.is_legal(Move::from_move16(mv))
    }
}
