use std::time::Duration;

use bevy::{
    prelude::*, 
    time::Stopwatch,
};
use bevy_kira_audio::{prelude::Volume, Audio, AudioControl, AudioTween};
use bevy_rapier3d::prelude::*;
use rand::prelude::*;

use crate::{
    asset_loading::AudioAssets,
    player::Player,
    GameState
};

// Monster states and behavior configuration
#[derive(Debug, Component)]
pub struct Monster {
    pub speed: f32,
    pub detection_range: f32,
    pub state_timer: Timer,
    pub footstep_timer: Timer,
    pub state: MonsterState,
}

#[derive(Debug, Clone, PartialEq)]
pub enum MonsterState {
    Lurking,
    Hunting, 
    Wandering,
    Dormant,
}

impl Default for Monster {
    fn default() -> Self {
        Self {
            speed: 2.0,
            detection_range: 15.0,
            state_timer: Timer::from_seconds(1.0, TimerMode::Once),
            footstep_timer: Timer::from_seconds(3.0, TimerMode::Repeating),
            state: MonsterState::Dormant,
        }
    }
}

#[derive(Debug, Component, Default)]
pub struct MonsterPathfinding {
    pub current_target: Option<Vec3>,
    pub wander_target_timer: Timer,
}

#[derive(Debug, Component, Default)]
pub struct DangerIndicator {
    pub last_sound_timer: Stopwatch,
}

pub fn plugin(app: &mut App) {
    app
        .add_systems(OnEnter(GameState::Playing), spawn_monster)
        .add_systems(
            Update,
            (
                update_monster_state,
                move_monster,
                play_monster_sounds,
            ).run_if(in_state(GameState::Playing)),
        );
}

fn spawn_monster(
    mut commands: Commands,
    // mut meshes: ResMut<Assets<Mesh>>,
    // mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let spawn_position = Vec3::new(20.0, 0.5, 20.0);
    
    commands.spawn((
        Monster::default(),
        MonsterPathfinding {
            current_target: None,
            wander_target_timer: Timer::from_seconds(5.0, TimerMode::Repeating),
        },
        DangerIndicator::default(),
        RigidBody::Dynamic,
        Collider::capsule(Vec3::new(0.0, -0.5, 0.0), Vec3::new(0.0, 1.0, 0.0), 0.6),
        Velocity::zero(),
        LockedAxes::ROTATION_LOCKED,
        Damping {
            linear_damping: 1.0,
            angular_damping: 1.0,
        },
        Transform::from_translation(spawn_position),
    ));
}

fn update_monster_state(
    time: Res<Time>,
    mut monster_query: Query<(&mut Monster, &Transform, &mut MonsterPathfinding, &mut DangerIndicator), Without<Player>>,
    player_query: Query<&Transform, With<Player>>,
) {
    let mut rand = rand::rng();
    
    if let Ok((mut monster, monster_transform, mut pathfinding, mut danger)) = monster_query.get_single_mut() {
        let player_pos = if let Ok(player_transform) = player_query.get_single() {
            Some(player_transform.translation)
        } else {
            None
        };
        
        monster.state_timer.tick(time.delta());
        
        // when timer expires, potentially change state
        if monster.state_timer.just_finished() {
            let next_state = match monster.state {
                MonsterState::Dormant => {
                    // 70% chance to start lurking
                    if rand.random_bool(0.85) {
                        MonsterState::Lurking
                    } else {
                        MonsterState::Dormant
                    }
                },
                MonsterState::Lurking => {
                    // 30% chance to start hunting, 20% to go dormant, 50% to start wandering
                    let roll = rand.random_range(0.0..1.0);
                    if roll < 0.3 {
                        danger.last_sound_timer.reset();
                        MonsterState::Hunting
                    } else if roll < 0.5 {
                        MonsterState::Dormant
                    } else {
                        MonsterState::Wandering
                    }
                },
                MonsterState::Wandering => {
                    // 20% chance to start hunting, 30% to go lurking, 50% to keep wandering
                    let roll = rand.random_range(0.0..1.0);
                    if roll < 0.2 {
                        danger.last_sound_timer.reset();
                        MonsterState::Hunting
                    } else if roll < 0.5 {
                        MonsterState::Lurking
                    } else {
                        MonsterState::Wandering
                    }
                },
                MonsterState::Hunting => {
                    // after hunting, always go dormant for a while
                    MonsterState::Dormant
                }
            };
            
            // set state
            monster.state = next_state.clone();
            monster.state_timer = match next_state {
                MonsterState::Dormant => Timer::from_seconds(15.0, TimerMode::Once),
                MonsterState::Lurking => Timer::from_seconds(20.0, TimerMode::Once),
                MonsterState::Wandering => Timer::from_seconds(30.0, TimerMode::Once),
                MonsterState::Hunting => Timer::from_seconds(15.0, TimerMode::Once),
            };
            
            // reset pathfinding when state changes
            pathfinding.current_target = None;
            println!("new state={:?}", monster.state);
        }
        
        // override state in some cases
        if let Some(player_pos) = player_pos {
            let distance = monster_transform.translation.distance(player_pos);
            
            // if player is very close and monster isn't hunting, switch to hunting
            if distance < monster.detection_range * 0.5 && monster.state != MonsterState::Hunting {
                if rand.random_bool(0.75) {  // 75% chance to notice player
                    monster.state = MonsterState::Hunting;
                    monster.state_timer = Timer::from_seconds(20.0, TimerMode::Once);
                    danger.last_sound_timer.reset();
                    pathfinding.current_target = None;
                }
            }
        }        
    }
}

fn move_monster(
    time: Res<Time>,
    mut monster_query: Query<(&Monster, &mut Transform, &mut Velocity, &mut MonsterPathfinding), Without<Player>>,
    player_query: Query<&Transform, With<Player>>,
) {
    let mut rand = rand::rng();
    
    if let Ok((monster, mut transform, mut velocity, mut pathfinding)) = monster_query.get_single_mut() {
        pathfinding.wander_target_timer.tick(time.delta());
        
        // move based on state
        match monster.state {
            MonsterState::Dormant => {
                velocity.linvel = Vec3::ZERO;
            },
            MonsterState::Lurking => {
                // get a random wander target if we don't have one or timer expired
                if pathfinding.current_target.is_none() || pathfinding.wander_target_timer.just_finished() {
                    let range = 30.0;
                    let random_offset = Vec3::new(
                        rand.random_range(-range..range),
                        0.0,
                        rand.random_range(-range..range)
                    );
                    
                    let base_pos = if let Ok(player_transform) = player_query.get_single() {
                        player_transform.translation
                    } else {
                        transform.translation
                    };
                    
                    pathfinding.current_target = Some(base_pos + random_offset);
                    pathfinding.wander_target_timer = Timer::from_seconds(10.0, TimerMode::Once);
                }
                
                // move toward target at slow pace
                if let Some(target) = pathfinding.current_target {
                    move_towards_target(&mut transform, &mut velocity, target, monster.speed * 0.65);
                }
            },
            MonsterState::Wandering => {
                if pathfinding.current_target.is_none() || pathfinding.wander_target_timer.just_finished() {
                    if let Ok(player_transform) = player_query.get_single() {
                        // choose a random position somewhat close to player
                        let range = 15.0;
                        let random_offset = Vec3::new(
                            rand.random_range(-range..range),
                            0.0,
                            rand.random_range(-range..range)
                        );
                        
                        pathfinding.current_target = Some(player_transform.translation + random_offset);
                        pathfinding.wander_target_timer = Timer::from_seconds(7.0, TimerMode::Once);
                    }
                }
                
                // move toward target at medium pace
                if let Some(target) = pathfinding.current_target {
                    move_towards_target(&mut transform, &mut velocity, target, monster.speed * 0.85);
                }
            },
            MonsterState::Hunting => {
                // chase the motherfucker
                if let Ok(player_transform) = player_query.get_single() {
                    move_towards_target(
                        &mut transform, 
                        &mut velocity, 
                        player_transform.translation, 
                        monster.speed * 1.1
                    );
                }
            }
        }
    }
}

fn move_towards_target(
    transform: &mut Transform,
    velocity: &mut Velocity,
    target: Vec3,
    speed: f32
) {
    let direction = (target - transform.translation).normalize_or_zero();
    let direction = Vec3::new(direction.x, 0.0, direction.z).normalize_or_zero();
    velocity.linvel = direction * speed;
    
    if direction.length_squared() > 0.01 {
        let look_direction = Vec3::new(direction.x, 0.0, direction.z).normalize_or_zero();
        if look_direction != Vec3::ZERO {
            let target_rotation = Quat::from_rotation_arc(Vec3::Z, look_direction);
            transform.rotation = transform.rotation.slerp(target_rotation, 0.1);
        }
    }
}

fn play_monster_sounds(
    time: Res<Time>,
    audio: Res<Audio>,
    audio_assets: Res<AudioAssets>,
    mut monster_query: Query<(&mut Monster, &Transform), Without<Player>>,
    player_query: Query<&Transform, With<Player>>,
) {
    let mut rand = rand::rng();
    if let (Ok((mut monster, monster_transform)), Ok(player_transform)) = 
        (monster_query.get_single_mut(), player_query.get_single()) {

        monster.footstep_timer.tick(time.delta());
        let distance = monster_transform.translation.distance(player_transform.translation);
        
        // play footstep sound if the timer finished
        if monster.footstep_timer.just_finished() {
            match monster.state {
                MonsterState::Hunting => {
                    monster.footstep_timer = Timer::from_seconds(1.5, TimerMode::Once);
                },
                MonsterState::Lurking | MonsterState::Wandering => {
                    monster.footstep_timer = Timer::from_seconds(
                        rand.random_range(3.0..7.0),
                        TimerMode::Once
                    );
                },
                MonsterState::Dormant => {
                    monster.footstep_timer = Timer::from_seconds(
                        rand.random_range(10.0..20.0),
                        TimerMode::Once
                    );
                }
            }

            play_footstep_segment(audio, &audio_assets, distance, &monster.state, monster.footstep_timer.duration().as_secs_f32());

            if monster.state == MonsterState::Hunting && rand.random_bool(0.3) {
                println!("Monster growl!");
            }
        }
    }
}

fn play_footstep_segment(
    audio: Res<Audio>,
    audio_assets: &Res<AudioAssets>,
    distance: f32,
    state: &MonsterState,
    dur: f32
) {
    let base_volume: f32 = match state {
        MonsterState::Dormant => 0.6,
        MonsterState::Lurking => 0.65,
        MonsterState::Wandering => 0.8,
        MonsterState::Hunting => 1.0,
    };
    
    // adjust volume based on distance (closer = louder)
    let distance_factor: f32 = 1.0 - (distance / 100.0).clamp(0.0, 1.0);
    let volume = base_volume * distance_factor;
    
    // play only a short segment of the footstep sound
    // by only playing the last part
    let mut start_time = 30.0 - dur;
    // let mut rand = rand::rng();
    // start_time += rand.random_range(0.0..5.0);

    audio.play(audio_assets.monster_footsteps.clone())
        .with_volume(volume as f64)
        .start_from((start_time as f64).min(27.0))
        .fade_in(AudioTween::linear(Duration::from_millis(100)))
        .handle();
    
    println!("Monster footstep: State={:?}, Distance={:.2}, Volume={:.2}, Dur={:1}, Start={:1}", 
        state, distance, volume, dur, (start_time as f64).min(27.0));
}