use crate::core::config::{
    AnimationStep, NextStateEntry, StateConfig, StateMachineConfig, StateMode, StateTransition,
    TriggerType,
};
use anyhow::Result;
use rand::Rng;

pub struct StateMachine {
    pub config: StateMachineConfig,
    pub current_state: String,
    pub current_sequence_index: usize,
    pub current_loop_remaining: i32,
    pub pending_trigger_target: Option<String>,
}

impl StateMachine {
    pub fn new(config: StateMachineConfig) -> Self {
        Self {
            current_state: config.initial_state.clone(),
            config,
            current_sequence_index: 0,
            current_loop_remaining: 0,
            pending_trigger_target: None,
        }
    }

    pub fn start(&mut self) -> Result<()> {
        self.current_state = self.config.initial_state.clone();
        self.pending_trigger_target = None;
        self.reset_state_progress()
    }

    pub fn current_video_id(&self) -> Option<String> {
        self.current_state_config()
            .and_then(|state| state.sequence.get(self.current_sequence_index))
            .map(|step| step.video_id.clone())
    }

    pub fn current_state_config(&self) -> Option<&StateConfig> {
        self.config.states.get(&self.current_state)
    }

    pub fn on_video_completed(&mut self) -> Result<bool> {
        self.ensure_current_state_valid()?;

        let old_state = self.current_state.clone();

        if self.current_loop_remaining > 1 {
            self.current_loop_remaining -= 1;
            return Ok(false);
        }

        // 有待执行的触发器时，跳过 sequence 剩余 step，立即切换状态
        if let Some(target_state) = self.pending_trigger_target.take() {
            self.transition_to_state(&target_state)?;
            return Ok(self.current_state != old_state);
        }

        let sequence_len = self
            .current_state_config()
            .ok_or_else(|| anyhow::anyhow!("当前状态不存在: {}", self.current_state))?
            .sequence
            .len();
        if self.current_sequence_index + 1 < sequence_len {
            self.current_sequence_index += 1;
            self.current_loop_remaining = self.resolve_current_loop_count()?;
            return Ok(false);
        }

        if let Some(target_state) = self.pending_trigger_target.take() {
            self.transition_to_state(&target_state)?;
            return Ok(self.current_state != old_state);
        }

        let next_state = self.select_next_state()?;
        self.transition_to_state(&next_state)?;
        Ok(self.current_state != old_state)
    }

    pub fn handle_trigger(&mut self, name: &str, value: &str) -> Result<bool> {
        let Some(state) = self.current_state_config() else {
            return Ok(false);
        };

        if state.ignore_triggers {
            return Ok(false);
        }

        if self.pending_trigger_target.is_some() {
            return Ok(false);
        }

        let target_state = self
            .matching_transition(&state.transitions, name, value)
            .map(|transition| transition.target_state.clone());

        let Some(target_state) = target_state else {
            return Ok(false);
        };

        if state.defer_triggers {
            self.pending_trigger_target = Some(target_state);
            if self.current_loop_remaining > 1 {
                self.current_loop_remaining = 1;
            }
            return Ok(false);
        }

        self.transition_to_state(&target_state)?;
        Ok(true)
    }

    pub fn check_random_triggers(&mut self) -> Result<bool> {
        let Some(state) = self.current_state_config() else {
            return Ok(false);
        };

        if state.ignore_triggers {
            return Ok(false);
        }

        if !matches!(state.mode, StateMode::FreeMode | StateMode::InteractiveMode) {
            return Ok(false);
        }

        let mut rng = rand::thread_rng();
        let target_state = state
            .transitions
            .iter()
            .filter(|transition| match &transition.trigger {
                TriggerType::Random { probability } => {
                    let probability = probability.clamp(0.0, 1.0);
                    rng.gen_bool(probability)
                }
                _ => false,
            })
            .max_by_key(|transition| transition.priority)
            .map(|transition| transition.target_state.clone());

        let Some(target_state) = target_state else {
            return Ok(false);
        };

        if state.defer_triggers {
            self.pending_trigger_target = Some(target_state);
            return Ok(true);
        }

        self.transition_to_state(&target_state)?;
        Ok(true)
    }

    pub fn has_pending_trigger(&self) -> bool {
        self.pending_trigger_target.is_some()
    }

    fn reset_state_progress(&mut self) -> Result<()> {
        self.current_sequence_index = 0;
        self.ensure_current_state_valid()?;
        self.current_loop_remaining = self.resolve_current_loop_count()?;
        Ok(())
    }

    fn transition_to_state(&mut self, target_state: &str) -> Result<()> {
        if !self.config.states.contains_key(target_state) {
            anyhow::bail!("目标状态不存在: {}", target_state);
        }

        self.current_state = target_state.to_string();
        self.pending_trigger_target = None;
        self.reset_state_progress()
    }

    fn ensure_current_state_valid(&self) -> Result<()> {
        let state = self
            .current_state_config()
            .ok_or_else(|| anyhow::anyhow!("当前状态不存在: {}", self.current_state))?;

        if state.sequence.is_empty() {
            anyhow::bail!("状态 '{}' 的 sequence 不能为空", state.name);
        }

        if self.current_sequence_index >= state.sequence.len() {
            anyhow::bail!(
                "状态 '{}' 的 sequence 索引越界: {} >= {}",
                state.name,
                self.current_sequence_index,
                state.sequence.len()
            );
        }

        Ok(())
    }

    fn resolve_current_loop_count(&self) -> Result<i32> {
        if self.pending_trigger_target.is_some() {
            return Ok(1);
        }

        let state = self
            .current_state_config()
            .ok_or_else(|| anyhow::anyhow!("当前状态不存在: {}", self.current_state))?;
        let step = state
            .sequence
            .get(self.current_sequence_index)
            .ok_or_else(|| {
                anyhow::anyhow!(
                    "状态 '{}' 的 sequence 索引越界: {}",
                    state.name,
                    self.current_sequence_index
                )
            })?;

        Ok(self.resolve_step_loop_count(step))
    }

    fn resolve_step_loop_count(&self, step: &AnimationStep) -> i32 {
        if let Some([start, end]) = step.random_loop_range {
            let min = start.min(end);
            let max = start.max(end);
            return rand::thread_rng().gen_range(min..=max).max(1);
        }

        step.loop_count.unwrap_or(1).max(1)
    }

    fn select_next_state(&self) -> Result<String> {
        let state = self
            .current_state_config()
            .ok_or_else(|| anyhow::anyhow!("当前状态不存在: {}", self.current_state))?;

        if let Some(entries) = &state.next_states {
            if let Some(next_state) = self.select_weighted_next_state(entries) {
                return Ok(next_state);
            }
        }

        if let Some(next_state) = &state.next_state {
            return Ok(next_state.clone());
        }

        // FreeMode 状态没有配置 next_state/next_states 时，按权重随机选择一个 FreeMode 状态
        match state.mode {
            StateMode::FreeMode => self.select_random_free_state(),
            StateMode::InteractiveMode => Ok(self.current_state.clone()),
        }
    }

    /// 按权重随机选择一个 FreeMode 状态（旧版行为回补）
    fn select_random_free_state(&self) -> Result<String> {
        let free_states: Vec<(&String, &StateConfig)> = self
            .config
            .states
            .iter()
            .filter(|(_, state)| matches!(state.mode, StateMode::FreeMode))
            .collect();

        if free_states.is_empty() {
            anyhow::bail!("没有可用的 FreeMode 状态");
        }

        let total_weight: f32 = free_states.iter().map(|(_, state)| state.weight).sum();
        if total_weight <= 0.0 {
            return Ok(free_states[0].0.clone());
        }

        let mut cursor = rand::thread_rng().gen_range(0.0..total_weight);
        for (name, state) in &free_states {
            cursor -= state.weight;
            if cursor <= 0.0 {
                return Ok((*name).clone());
            }
        }

        // free_states 非空已由上方 bail! 保证
        Ok(free_states.last().expect("free_states is non-empty").0.clone())
    }

    fn select_weighted_next_state(&self, entries: &[NextStateEntry]) -> Option<String> {
        let valid_entries: Vec<&NextStateEntry> =
            entries.iter().filter(|entry| entry.weight > 0.0).collect();
        if valid_entries.is_empty() {
            return None;
        }

        let total_weight: f32 = valid_entries.iter().map(|entry| entry.weight).sum();
        if total_weight <= 0.0 {
            return None;
        }

        let mut cursor = rand::thread_rng().gen_range(0.0..total_weight);
        for entry in &valid_entries {
            cursor -= entry.weight;
            if cursor <= 0.0 {
                return Some(entry.state.clone());
            }
        }

        valid_entries.last().map(|entry| entry.state.clone())
    }

    fn matching_transition<'a>(
        &self,
        transitions: &'a [StateTransition],
        name: &str,
        value: &str,
    ) -> Option<&'a StateTransition> {
        transitions
            .iter()
            .filter(|transition| Self::trigger_matches(&transition.trigger, name, value))
            .max_by_key(|transition| transition.priority)
    }

    fn trigger_matches(trigger: &TriggerType, name: &str, value: &str) -> bool {
        match trigger {
            TriggerType::Button { name: expected } => {
                (name == "button" && value == expected) || expected == name
            }
            TriggerType::Voice { keyword } => keyword == value || keyword == name,
            TriggerType::Sensor { name: expected } => {
                (name == "sensor" && value == expected) || expected == name
            }
            TriggerType::Timer { .. } | TriggerType::Random { .. } => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::StateMachine;
    use crate::core::config::{
        AnimationStep, NextStateEntry, StateConfig, StateMachineConfig, StateMode, StateTransition,
        TriggerType,
    };
    use std::collections::HashMap;

    #[test]
    fn button_and_sensor_triggers_match_legacy_message_shape() {
        let mut machine = StateMachine::new(config_with_states([
            state(
                "idle",
                vec![step("idle")],
                vec![
                    transition(
                        TriggerType::Button {
                            name: "button1".to_string(),
                        },
                        "button_state",
                        10,
                    ),
                    transition(
                        TriggerType::Sensor {
                            name: "touch".to_string(),
                        },
                        "sensor_state",
                        10,
                    ),
                ],
            ),
            state("button_state", vec![step("button")], vec![]),
            state("sensor_state", vec![step("sensor")], vec![]),
        ]));
        machine.start().expect("state machine should start");

        assert!(machine
            .handle_trigger("button", "button1")
            .expect("button trigger should work"));
        assert_eq!(machine.current_state, "button_state");

        machine.transition_to_state("idle").expect("reset to idle");
        assert!(machine
            .handle_trigger("sensor", "touch")
            .expect("sensor trigger should work"));
        assert_eq!(machine.current_state, "sensor_state");
    }

    #[test]
    fn deferred_trigger_waits_for_sequence_end_and_forces_single_remaining_loops() {
        let mut machine = StateMachine::new(config_with_states([
            StateConfig {
                name: "idle".to_string(),
                mode: StateMode::FreeMode,
                sequence: vec![
                    AnimationStep {
                        video_id: "looping".to_string(),
                        loop_count: Some(3),
                        random_loop_range: None,
                    },
                    AnimationStep {
                        video_id: "tail".to_string(),
                        loop_count: Some(5),
                        random_loop_range: None,
                    },
                ],
                next_state: None,
                next_states: None,
                transitions: vec![transition(
                    TriggerType::Voice {
                        keyword: "name".to_string(),
                    },
                    "called",
                    10,
                )],
                weight: 1.0,
                defer_triggers: true,
                ignore_triggers: false,
            },
            state("called", vec![step("called")], vec![]),
        ]));
        machine.start().expect("state machine should start");

        assert_eq!(machine.current_loop_remaining, 3);
        assert!(!machine
            .handle_trigger("voice", "name")
            .expect("deferred trigger should queue"));
        assert!(machine.has_pending_trigger());
        assert_eq!(machine.current_state, "idle");
        assert_eq!(machine.current_loop_remaining, 1);

        // 当前 step 播完后，有 pending trigger 应立即跳转，跳过后续 step
        assert!(machine
            .on_video_completed()
            .expect("pending trigger should fire after current step ends"));
        assert_eq!(machine.current_state, "called");
        assert_eq!(machine.current_video_id().as_deref(), Some("called"));
        assert!(!machine.has_pending_trigger());
    }

    #[test]
    fn on_video_completed_returns_false_when_staying_in_same_state() {
        let mut machine = StateMachine::new(config_with_states([StateConfig {
            name: "idle".to_string(),
            mode: StateMode::FreeMode,
            sequence: vec![step("a"), step("b")],
            next_state: Some("idle".to_string()),
            next_states: None,
            transitions: vec![],
            weight: 1.0,
            defer_triggers: false,
            ignore_triggers: false,
        }]));
        machine.start().expect("state machine should start");

        assert!(!machine
            .on_video_completed()
            .expect("step transition should stay in same state"));
        assert_eq!(machine.current_video_id().as_deref(), Some("b"));
    }

    #[test]
    fn next_states_take_priority_over_next_state() {
        let mut machine = StateMachine::new(config_with_states([
            StateConfig {
                name: "idle".to_string(),
                mode: StateMode::FreeMode,
                sequence: vec![step("idle")],
                next_state: Some("fallback".to_string()),
                next_states: Some(vec![NextStateEntry {
                    state: "weighted".to_string(),
                    weight: 1.0,
                }]),
                transitions: vec![],
                weight: 1.0,
                defer_triggers: false,
                ignore_triggers: false,
            },
            state("weighted", vec![step("weighted")], vec![]),
            state("fallback", vec![step("fallback")], vec![]),
        ]));
        machine.start().expect("state machine should start");

        assert!(machine
            .on_video_completed()
            .expect("state should change via next_states"));
        assert_eq!(machine.current_state, "weighted");
    }

    #[test]
    fn free_mode_random_walk_when_no_next_state_configured() {
        let mut machine = StateMachine::new(config_with_states([
            StateConfig {
                name: "idle".to_string(),
                mode: StateMode::FreeMode,
                sequence: vec![step("idle")],
                next_state: None,
                next_states: None,
                transitions: vec![],
                weight: 2.0,
                defer_triggers: false,
                ignore_triggers: false,
            },
            StateConfig {
                name: "walk".to_string(),
                mode: StateMode::FreeMode,
                sequence: vec![step("walk")],
                next_state: None,
                next_states: None,
                transitions: vec![],
                weight: 3.0,
                defer_triggers: false,
                ignore_triggers: false,
            },
            StateConfig {
                name: "interactive".to_string(),
                mode: StateMode::InteractiveMode,
                sequence: vec![step("interactive")],
                next_state: None,
                next_states: None,
                transitions: vec![],
                weight: 1.0,
                defer_triggers: false,
                ignore_triggers: false,
            },
        ]));
        machine.start().expect("state machine should start");

        let mut visited_states = std::collections::HashSet::new();
        for _ in 0..20 {
            machine
                .on_video_completed()
                .expect("should transition to random free state");
            visited_states.insert(machine.current_state.clone());

            // 验证只会跳转到 FreeMode 状态
            assert!(
                machine.current_state == "idle" || machine.current_state == "walk",
                "应该只跳转到 FreeMode 状态，但跳转到了: {}",
                machine.current_state
            );
        }

        // 验证随机性：20 次跳转应该至少访问过两个状态
        assert!(
            visited_states.len() >= 2,
            "随机游走应该访问多个状态，但只访问了: {:?}",
            visited_states
        );
    }

    #[test]
    fn interactive_mode_stays_in_same_state_when_no_next_state_configured() {
        let states = vec![StateConfig {
            name: "interactive".to_string(),
            mode: StateMode::InteractiveMode,
            sequence: vec![step("interactive")],
            next_state: None,
            next_states: None,
            transitions: vec![],
            weight: 1.0,
            defer_triggers: false,
            ignore_triggers: false,
        }];

        let states_map = states
            .into_iter()
            .map(|state| {
                let name = state.name.clone();
                (name, state)
            })
            .collect::<HashMap<_, _>>();

        let mut machine = StateMachine::new(StateMachineConfig {
            initial_state: "interactive".to_string(),
            states: states_map,
        });
        machine.start().expect("state machine should start");

        assert!(!machine
            .on_video_completed()
            .expect("interactive mode should stay in same state"));
        assert_eq!(machine.current_state, "interactive");
    }

    fn config_with_states<const N: usize>(states: [StateConfig; N]) -> StateMachineConfig {
        let states = Vec::from(states)
            .into_iter()
            .map(|state| {
                let name = state.name.clone();
                (name, state)
            })
            .collect::<HashMap<_, _>>();

        StateMachineConfig {
            initial_state: "idle".to_string(),
            states,
        }
    }

    fn state(
        name: &str,
        sequence: Vec<AnimationStep>,
        transitions: Vec<StateTransition>,
    ) -> StateConfig {
        StateConfig {
            name: name.to_string(),
            mode: StateMode::FreeMode,
            sequence,
            next_state: None,
            next_states: None,
            transitions,
            weight: 1.0,
            defer_triggers: false,
            ignore_triggers: false,
        }
    }

    fn step(video_id: &str) -> AnimationStep {
        AnimationStep {
            video_id: video_id.to_string(),
            loop_count: Some(1),
            random_loop_range: None,
        }
    }

    fn transition(trigger: TriggerType, target_state: &str, priority: i32) -> StateTransition {
        StateTransition {
            trigger,
            target_state: target_state.to_string(),
            priority,
        }
    }
}
