use std::f32::consts::PI;

use avian3d::prelude::*;
use bevy::{
    ecs::{component::HookContext, world::DeferredWorld},
    math::*,
    prelude::*,
};
use bevy_trenchbroom::prelude::*;

use crate::cash::Cash;
use crate::loading_assets::GameAssets;
use crate::physics_layers::GameLayer;

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
#[component(on_add = Self::on_add)]
#[model({ "path": "models/spawn.glb" })]
pub struct SpawnPoint {}

impl SpawnPoint {
    fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
        let Some(assets) = world.get_resource::<GameAssets>() else {
            return;
        };

        let mesh = assets.mesh_spawn.clone();

        world.commands().entity(entity).insert((
            Name::new("spawn"),
            SceneRoot(mesh),
            RigidBody::Static,
            ColliderConstructorHierarchy::new(ColliderConstructor::ConvexHullFromMesh),
        ));
    }
}

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[spawn_hooks(SpawnHooks::new().convex_collider())]
pub struct Worldspawn;

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[spawn_hooks(SpawnHooks::new())]
#[base(Transform)]
pub struct Water;

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
#[spawn_hooks(SpawnHooks::new().convex_collider())]
pub struct Crates;

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
#[spawn_hooks(SpawnHooks::new().convex_collider())]
pub struct NamedEntity {
    pub name: String,
}

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform, Target)]
#[spawn_hooks(SpawnHooks::new().convex_collider())]
pub struct Platform;

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
pub struct PlatformTarget {
    pub targetname: String,
}

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform, Target)]
#[spawn_hooks(SpawnHooks::new().convex_collider())]
pub struct Movable {
    pub name: String,
}

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
pub struct MoveTarget {
    pub targetname: String,
}

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
pub struct CameraTarget {
    pub targetname: String,
}

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform, Target)]
pub struct CutsceneCamera {
    pub name: String,
    pub targetname: String,
}

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform, Target)]
pub struct CutsceneCameraMovementEnd;

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
#[component(on_add = Self::on_add)]
#[model({ "path": "models/alien_naked.glb" })]
pub struct EnemySpawn {
    pub head: String,
    pub key: String,
    pub disable_ai: bool,
}

impl EnemySpawn {
    fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
        //TODO: figure out why this crashes if removed
        let Some(_assets) = world.get_resource::<GameAssets>() else {
            return;
        };

        let this = world.get_entity(entity).unwrap().get::<Self>().unwrap();
        let this_transform = world
            .get_entity(entity)
            .unwrap()
            .get::<Transform>()
            .unwrap();

        let mut this_transform = *this_transform;
        this_transform.translation += Vec3::new(0., 1.5, 0.);
        this_transform.rotate_y(PI);

        let head = this.head.clone();

        world.commands().entity(entity).insert((
            this_transform,
            Name::from(format!("enemy [{}]", head)),
            Visibility::default(),
            RigidBody::Dynamic,
            Collider::capsule(0.6, 2.),
            CollisionLayers::new(LayerMask(GameLayer::Npc.to_bits()), LayerMask::ALL),
            LockedAxes::new().lock_rotation_z().lock_rotation_x(),
        ));
    }
}

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
#[component(on_add = Self::on_add)]
#[model({ "path": "models/cash.glb" })]
pub struct CashSpawn {}

impl CashSpawn {
    fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
        let Some(assets) = world.get_resource::<GameAssets>() else {
            return;
        };

        let mesh = assets.mesh_cash.clone();

        world.commands().entity(entity).insert((
            Name::new("cash"),
            SceneRoot(mesh),
            Cash,
            Collider::cuboid(2., 3.0, 2.),
            CollisionLayers::new(LayerMask(GameLayer::Collectibles.to_bits()), LayerMask::ALL),
            CollisionEventsEnabled,
            Sensor,
        ));
    }
}

#[derive(PointClass, Component, Reflect, Default)]
#[reflect(QuakeClass, Component)]
#[base(Transform)]
#[model({ "path": "models/head_drop.glb" })]
pub struct SecretHead {
    pub head_id: usize,
}

pub fn plugin(app: &mut App) {
    app.register_type::<SpawnPoint>();
    app.register_type::<Worldspawn>();
    app.register_type::<Water>();
    app.register_type::<Crates>();
    app.register_type::<NamedEntity>();
    app.register_type::<Platform>();
    app.register_type::<PlatformTarget>();
    app.register_type::<Movable>();
    app.register_type::<MoveTarget>();
    app.register_type::<CameraTarget>();
    app.register_type::<CutsceneCamera>();
    app.register_type::<CutsceneCameraMovementEnd>();
    app.register_type::<EnemySpawn>();
    app.register_type::<CashSpawn>();
    app.register_type::<SecretHead>();
}
