use std::time::Instant;

use bevy::{
    prelude::{Component, ReflectResource, Resource, Vec3},
    reflect::Reflect,
};

#[derive(Debug, Resource)]
pub(crate) struct ActionDebugInstant(pub Instant);

impl Default for ActionDebugInstant {
    fn default() -> Self {
        ActionDebugInstant(Instant::now())
    }
}

#[derive(Debug, Component)]
pub(super) struct Tunneling {
    pub frames: usize,
    pub dir: Vec3,
}

impl Default for Tunneling {
    fn default() -> Self {
        Tunneling {
            frames: 15,
            dir: Vec3::ZERO,
        }
    }
}

#[derive(Debug, Resource, Reflect)]
#[reflect(Resource)]
pub struct MovementSettings {
    pub accel: f32,
    pub gravity: f32,
    pub sensitivity: f32,
}

impl Default for MovementSettings {
    fn default() -> Self {
        Self {
            sensitivity: 6.0,
            accel: 40.0,
            gravity: 9.8,
        }
    }
}

#[derive(Debug, Resource, Reflect)]
#[reflect(Resource)]
pub struct CatControllerSettings {
    pub kp: f32,
    pub kd: f32,
    pub ki: f32,
}

impl Default for CatControllerSettings {
    fn default() -> Self {
        Self {
            kp: 40.0,
            kd: 5.0,
            ki: 0.1,
        }
    }
}

#[derive(Component, Debug, Clone, Copy)]
pub struct CatControllerState {
    pub roll_integral: f32,
    pub roll_prev: f32,
    pub pitch_integral: f32,
    pub pitch_prev: f32,
    decay_factor: f32,
    roll_limit: f32,
    pitch_limit: f32,
}

impl Default for CatControllerState {
    fn default() -> Self {
        Self {
            roll_integral: Default::default(),
            roll_prev: Default::default(),
            pitch_integral: Default::default(),
            pitch_prev: Default::default(),
            decay_factor: 0.99,
            roll_limit: 1.5,
            pitch_limit: 0.1,
        }
    }
}

impl CatControllerState {
    pub fn decay(&mut self) {
        if self.roll_integral.abs() > 0.001 {
            self.roll_integral *= self.decay_factor;
        }
        if self.pitch_integral.abs() > 0.001 {
            self.pitch_integral *= self.decay_factor;
        }
    }

    pub fn update_roll(&mut self, error: f32, dt: f32) -> (f32, f32) {
        let lim = self.roll_limit;
        self.roll_integral = (self.roll_integral + (error * dt)).min(lim).max(-lim);
        let derivative = (error - self.roll_prev) / dt;
        self.roll_prev = error;
        (derivative, self.roll_integral)
    }

    pub fn update_pitch(&mut self, error: f32, dt: f32) -> (f32, f32) {
        let lim = self.pitch_limit;
        self.pitch_integral = (self.pitch_integral + (error * dt)).min(lim).max(-lim);
        let derivative = (error - self.pitch_prev) / dt;
        self.pitch_prev = error;
        (derivative, self.pitch_integral)
    }

    pub fn set_integral_limits(&mut self, roll: f32, pitch: f32) {
        self.roll_limit = roll;
        self.pitch_limit = pitch;
    }
}
