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

use crate::cash::Cash;

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

impl SpawnPoint {
    fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
        let Some(asset_server) = world.get_resource::<AssetServer>() else {
            return;
        };

        let mesh = asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/spawn.glb"));

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

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(Component)]
#[geometry(GeometryProvider::new().trimesh_collider().render())]
pub struct Worldspawn;

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(Component)]
#[require(Transform)]
#[geometry(GeometryProvider::new().trimesh_collider().render())]
pub struct Crates;

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(Component)]
#[require(Transform)]
#[geometry(GeometryProvider::new().trimesh_collider().render())]
pub struct NamedEntity {
    pub name: String,
}

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(Component)]
#[require(Transform, Target)]
#[geometry(GeometryProvider::new().trimesh_collider().render())]
pub struct Platform;

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

#[derive(SolidClass, Component, Reflect, Default)]
#[reflect(Component)]
#[require(Transform, Target)]
#[geometry(GeometryProvider::new().trimesh_collider().render())]
pub struct Movable {
    pub name: String,
}

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

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

impl EnemySpawn {
    fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
        let Some(asset_server) = world.get_resource::<AssetServer>() 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., 0.);

        let mesh = asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/alien_naked.glb"));

        let head_mesh = asset_server
            .load(GltfAssetLabel::Scene(0).from_asset(format!("models/heads/{}.glb", this.head)));

        world
            .commands()
            .entity(entity)
            .insert((
                this_transform,
                Name::from("Enemy"),
                Visibility::default(),
                RigidBody::Dynamic,
                Collider::capsule(0.4, 2.),
                LockedAxes::new().lock_rotation_z().lock_rotation_x(),
            ))
            .with_children(|parent| {
                parent
                    .spawn((
                        Transform::from_translation(Vec3::new(0., 1., 0.)),
                        SceneRoot(head_mesh),
                    ))
                    .with_child((
                        Visibility::default(),
                        Transform::from_translation(Vec3::new(0., -2.4, 0.))
                            .with_scale(Vec3::splat(1.5)),
                        SceneRoot(mesh),
                    ));
            });
    }
}

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

impl CashSpawn {
    fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
        let Some(asset_server) = world.get_resource::<AssetServer>() else {
            return;
        };

        let mesh = asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/cash.glb"));

        world.commands().entity(entity).insert((
            Name::new("cash"),
            SceneRoot(mesh),
            Cash,
            Collider::cuboid(2., 3.0, 2.),
            Sensor,
        ));
    }
}
