use crate::ecs::material::components::Material;
use crate::ecs::physics::components::{ColliderComponent, ColliderShape, RigidBodyComponent};
use crate::ecs::physics::types::{InteractionGroups, LockedAxes, RigidBodyType};
use crate::ecs::script::components::Script;
use crate::ecs::world::components::{
    BoundingVolume, Camera, LocalTransform, Name, RenderMesh, Visibility,
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PrefabBodyType {
    Dynamic,
    Kinematic,
    Static,
}

pub fn prefab_body_type_to_rigid_body_type(body_type: PrefabBodyType) -> RigidBodyType {
    match body_type {
        PrefabBodyType::Dynamic => RigidBodyType::Dynamic,
        PrefabBodyType::Kinematic => RigidBodyType::KinematicPositionBased,
        PrefabBodyType::Static => RigidBodyType::Fixed,
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefabRigidBody {
    pub body_type: PrefabBodyType,
    pub mass: f32,
}

impl Default for PrefabRigidBody {
    fn default() -> Self {
        Self {
            body_type: PrefabBodyType::Dynamic,
            mass: 1.0,
        }
    }
}

pub fn prefab_rigid_body_to_component(prefab: &PrefabRigidBody) -> RigidBodyComponent {
    RigidBodyComponent {
        handle: None,
        body_type: prefab_body_type_to_rigid_body_type(prefab.body_type),
        translation: [0.0, 0.0, 0.0],
        rotation: [0.0, 0.0, 0.0, 1.0],
        linvel: [0.0, 0.0, 0.0],
        angvel: [0.0, 0.0, 0.0],
        mass: prefab.mass,
        locked_axes: LockedAxes::empty(),
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefabCollider {
    pub shape: ColliderShape,
    pub friction: f32,
    pub restitution: f32,
    pub density: f32,
    pub is_sensor: bool,
}

impl Default for PrefabCollider {
    fn default() -> Self {
        Self {
            shape: ColliderShape::Cuboid {
                hx: 0.5,
                hy: 0.5,
                hz: 0.5,
            },
            friction: 0.5,
            restitution: 0.0,
            density: 1.0,
            is_sensor: false,
        }
    }
}

pub fn prefab_collider_to_component(prefab: &PrefabCollider) -> ColliderComponent {
    ColliderComponent {
        handle: None,
        shape: prefab.shape.clone(),
        friction: prefab.friction,
        restitution: prefab.restitution,
        density: prefab.density,
        is_sensor: prefab.is_sensor,
        collision_groups: InteractionGroups::all(),
        solver_groups: InteractionGroups::all(),
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Prefab {
    pub name: String,
    pub root_nodes: Vec<PrefabNode>,
}

pub fn prefab_new(name: String) -> Prefab {
    Prefab {
        name,
        root_nodes: Vec::new(),
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefabNode {
    pub local_transform: LocalTransform,
    pub components: PrefabComponents,
    pub children: Vec<PrefabNode>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub node_index: Option<usize>,
}

pub fn prefab_node_new(local_transform: LocalTransform) -> PrefabNode {
    PrefabNode {
        local_transform,
        components: PrefabComponents::default(),
        children: Vec::new(),
        node_index: None,
    }
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PrefabSource {
    pub prefab_name: String,
    pub source_path: Option<String>,
}

pub fn prefab_source_new(prefab_name: impl Into<String>) -> PrefabSource {
    PrefabSource {
        prefab_name: prefab_name.into(),
        source_path: None,
    }
}

pub fn prefab_source_with_path(
    prefab_name: impl Into<String>,
    source_path: impl Into<String>,
) -> PrefabSource {
    PrefabSource {
        prefab_name: prefab_name.into(),
        source_path: Some(source_path.into()),
    }
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PrefabComponents {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<Name>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub render_mesh: Option<RenderMesh>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub material: Option<Material>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub camera: Option<Camera>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub visibility: Option<Visibility>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub bounding_volume: Option<BoundingVolume>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub rigid_body: Option<PrefabRigidBody>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub collider: Option<PrefabCollider>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub script: Option<Script>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub skin_index: Option<usize>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefabInstance {
    pub prefab_id: PrefabId,
    pub instance_id: uuid::Uuid,
    pub overrides: PrefabOverrides,
    pub node_path: NodePath,
}

impl Default for PrefabInstance {
    fn default() -> Self {
        Self {
            prefab_id: PrefabId::default(),
            instance_id: uuid::Uuid::nil(),
            overrides: PrefabOverrides::default(),
            node_path: node_path_root(),
        }
    }
}

pub fn prefab_instance_new(prefab_id: PrefabId) -> PrefabInstance {
    PrefabInstance {
        prefab_id,
        instance_id: uuid::Uuid::new_v4(),
        overrides: PrefabOverrides::default(),
        node_path: node_path_root(),
    }
}

pub fn prefab_instance_with_path(prefab_id: PrefabId, node_path: NodePath) -> PrefabInstance {
    PrefabInstance {
        prefab_id,
        instance_id: uuid::Uuid::new_v4(),
        overrides: PrefabOverrides::default(),
        node_path,
    }
}

pub fn prefab_instance_has_overrides(instance: &PrefabInstance) -> bool {
    !prefab_overrides_is_empty(&instance.overrides)
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
pub struct PrefabId {
    pub name: String,
    pub source_path: Option<String>,
}

pub fn prefab_id_new(name: impl Into<String>) -> PrefabId {
    PrefabId {
        name: name.into(),
        source_path: None,
    }
}

pub fn prefab_id_with_path(name: impl Into<String>, path: impl Into<String>) -> PrefabId {
    PrefabId {
        name: name.into(),
        source_path: Some(path.into()),
    }
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PrefabOverrides {
    #[serde(skip_serializing_if = "TransformOverride::is_none")]
    pub transform_override: TransformOverride,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub component_overrides: Vec<ComponentOverride>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub added_components: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub removed_components: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub child_additions: Vec<ChildAddition>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub child_removals: Vec<NodePath>,
}

pub fn prefab_overrides_is_empty(overrides: &PrefabOverrides) -> bool {
    transform_override_is_none(&overrides.transform_override)
        && overrides.component_overrides.is_empty()
        && overrides.added_components.is_empty()
        && overrides.removed_components.is_empty()
        && overrides.child_additions.is_empty()
        && overrides.child_removals.is_empty()
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TransformOverride {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub translation: Option<[f32; 3]>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rotation: Option<[f32; 4]>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scale: Option<[f32; 3]>,
}

pub fn transform_override_is_none(override_data: &TransformOverride) -> bool {
    override_data.translation.is_none()
        && override_data.rotation.is_none()
        && override_data.scale.is_none()
}

impl TransformOverride {
    fn is_none(&self) -> bool {
        transform_override_is_none(self)
    }
}

pub fn transform_override_from_transforms(
    base: &LocalTransform,
    current: &LocalTransform,
) -> TransformOverride {
    let translation = if (base.translation - current.translation).norm() > f32::EPSILON {
        Some([
            current.translation.x,
            current.translation.y,
            current.translation.z,
        ])
    } else {
        None
    };

    let rotation_diff = base.rotation.try_inverse().unwrap_or(base.rotation) * current.rotation;
    let scalar_clamped = rotation_diff.scalar().clamp(-1.0, 1.0);
    let angle: f32 = 2.0 * scalar_clamped.acos();
    let rotation = if angle.abs() > f32::EPSILON {
        Some([
            current.rotation.i,
            current.rotation.j,
            current.rotation.k,
            current.rotation.w,
        ])
    } else {
        None
    };

    let scale = if (base.scale - current.scale).norm() > f32::EPSILON {
        Some([current.scale.x, current.scale.y, current.scale.z])
    } else {
        None
    };

    TransformOverride {
        translation,
        rotation,
        scale,
    }
}

pub fn transform_override_apply(
    override_data: &TransformOverride,
    transform: &LocalTransform,
) -> LocalTransform {
    let mut result = *transform;

    if let Some(translation) = override_data.translation {
        result.translation =
            nalgebra_glm::Vec3::new(translation[0], translation[1], translation[2]);
    }

    if let Some(rotation) = override_data.rotation {
        result.rotation =
            nalgebra_glm::Quat::new(rotation[3], rotation[0], rotation[1], rotation[2]);
    }

    if let Some(scale) = override_data.scale {
        result.scale = nalgebra_glm::Vec3::new(scale[0], scale[1], scale[2]);
    }

    result
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentOverride {
    pub component_type: String,
    pub overridden_fields: Vec<FieldOverride>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldOverride {
    pub field_name: String,
    pub value: serde_json::Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChildAddition {
    pub name: Option<String>,
    pub transform: LocalTransform,
    pub components: PrefabComponents,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct NodePath {
    pub segments: Vec<NodePathSegment>,
}

pub fn node_path_root() -> NodePath {
    NodePath {
        segments: Vec::new(),
    }
}

pub fn node_path_push(path: &mut NodePath, segment: NodePathSegment) {
    path.segments.push(segment);
}

pub fn node_path_child(path: NodePath, name: impl Into<String>, index: usize) -> NodePath {
    let mut result = path;
    result.segments.push(NodePathSegment {
        name: name.into(),
        index,
    });
    result
}

pub fn node_path_is_root(path: &NodePath) -> bool {
    path.segments.is_empty()
}

pub fn node_path_parent(path: &NodePath) -> Option<NodePath> {
    if path.segments.is_empty() {
        None
    } else {
        let mut parent = path.clone();
        parent.segments.pop();
        Some(parent)
    }
}

impl std::fmt::Display for NodePath {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.segments.is_empty() {
            write!(f, "/")
        } else {
            for segment in &self.segments {
                write!(f, "/{}", segment.name)?;
                if segment.index > 0 {
                    write!(f, "[{}]", segment.index)?;
                }
            }
            Ok(())
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct NodePathSegment {
    pub name: String,
    pub index: usize,
}
