#![deny(clippy::all, clippy::pedantic)]

use std::any::{type_name, TypeId};
use std::cell::RefCell;
use std::fmt::Debug;
use std::mem::ManuallyDrop;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

use hashbrown::HashMap;

use crate::actions::Action;
use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent;
use crate::component::storage::Storage as ComponentStorage;
use crate::component::{Component, Sequence as ComponentSequence};
use crate::entity::CREATE_STATIC_ENTITIES;
use crate::event::component::Kind as ComponentEventKind;
use crate::extension::{Collector as ExtensionCollector, Extension};
use crate::lock::{Lock, WriteGuard};
use crate::phase::{Phase, START as START_PHASE};
use crate::query::flexible::Query as FlexibleQuery;
use crate::query::term::Without;
use crate::query::{
    Iter as QueryIter,
    TermWithFieldTuple as QueryTermWithFieldTuple,
    TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
    Terms as QueryTerms,
    TermsBuilderInterface,
};
use crate::relationship::{ChildOf, DependsOn, Relationship};
use crate::sole::Sole;
use crate::stats::Stats;
use crate::system::{System, SystemComponent};
use crate::uid::{Kind as UidKind, Uid};

pub mod actions;
pub mod component;
pub mod entity;
pub mod event;
pub mod extension;
pub mod lock;
pub mod phase;
pub mod query;
pub mod relationship;
pub mod sole;
pub mod stats;
pub mod system;
pub mod tuple;
pub mod uid;
pub mod util;

#[doc(hidden)]
pub mod private;

pub use ecs_macros::{Component, Sole};

pub use crate::query::Query;

#[derive(Debug)]
pub struct World
{
    data: WorldData,
    stop: AtomicBool,
    is_first_tick: AtomicBool,
}

impl World
{
    #[must_use]
    pub fn new() -> Self
    {
        let mut world = Self {
            data: WorldData::default(),
            stop: AtomicBool::new(false),
            is_first_tick: AtomicBool::new(false),
        };

        world.add_sole(Stats::default()).ok();

        for create_static_entity in CREATE_STATIC_ENTITIES {
            create_static_entity(&world);
        }

        world
    }

    /// Creates a new entity with the given components.
    ///
    /// # Panics
    /// Will panic if mutable internal lock cannot be acquired.
    pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid
    where
        Comps: ComponentSequence,
    {
        let entity_uid = Uid::new_unique(UidKind::Entity);

        self.create_entity_with_uid(components, entity_uid);

        entity_uid
    }

    #[tracing::instrument(skip_all)]
    #[doc(hidden)]
    pub fn create_entity_with_uid<Comps>(&self, components: Comps, entity_uid: Uid)
    where
        Comps: ComponentSequence,
    {
        debug_assert_eq!(entity_uid.kind(), UidKind::Entity);

        {
            let mut component_storage_lock = self.lock_component_storage_rw();

            if let Err(err) = component_storage_lock.create_entity(entity_uid) {
                tracing::warn!("Failed to create entity: {err}");
                return;
            };

            Self::add_entity_components(
                entity_uid,
                components.into_array(),
                &mut component_storage_lock,
            );
        }

        for added_event_id in Comps::added_event_ids() {
            self.emit_event_by_id(added_event_id);
        }
    }

    /// Adds a globally shared singleton value.
    ///
    /// # Errors
    /// Returns `Err` if this [`Sole`] has already been added.
    pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
    where
        SoleT: Sole,
    {
        self.data.sole_storage.insert(sole)
    }

    pub fn register_system<'this, SystemImpl>(
        &'this mut self,
        phase_euid: Uid,
        system: impl System<'this, SystemImpl>,
    )
    {
        self.create_entity((
            SystemComponent { system: system.into_type_erased() },
            Relationship::<DependsOn, Phase>::new(phase_euid),
        ));
    }

    pub fn register_observer_system<'this, SystemImpl, Event>(
        &'this mut self,
        system: impl System<'this, SystemImpl>,
        event: Event,
    ) where
        Event: Component,
    {
        self.create_entity::<(SystemComponent, Event)>((
            SystemComponent { system: system.into_type_erased() },
            event,
        ));
    }

    /// Adds a extensions.
    ///
    /// # Panics
    /// Will panic if mutable internal lock cannot be acquired.
    pub fn add_extension(&mut self, extension: impl Extension)
    {
        let extension_collector = ExtensionCollector::new(self);

        extension.collect(extension_collector);
    }

    pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms>
    where
        FieldTerms: QueryTermWithFieldTuple,
        FieldlessTerms: QueryTermWithoutFieldTuple,
    {
        Query::new(self)
    }

    pub fn flexible_query<const MAX_TERM_CNT: usize>(
        &self,
        terms: QueryTerms<MAX_TERM_CNT>,
    ) -> FlexibleQuery<'_, MAX_TERM_CNT>
    {
        FlexibleQuery::new(self, terms)
    }

    /// Performs a single tick.
    ///
    /// # Panics
    /// Will panic if a internal lock cannot be acquired.
    pub fn step(&self) -> StepResult
    {
        if self.stop.load(Ordering::Relaxed) {
            return StepResult::Stop;
        }

        if self
            .is_first_tick
            .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
            .is_ok()
        {
            self.query_and_run_systems(*START_PHASE);
        }

        self.perform_phases();

        self.lock_component_storage_rw()
            .create_imaginary_archetypes();

        self.perform_queued_actions();

        if self.stop.load(Ordering::Relaxed) {
            return StepResult::Stop;
        }

        let mut stats_lock = self
            .data
            .sole_storage
            .get::<Stats>()
            .expect("No stats sole found")
            .write_nonblock()
            .expect("Failed to aquire read-write stats sole lock");

        let stats = stats_lock
            .downcast_mut::<Stats>()
            .expect("Casting stats sole to Stats type failed");

        stats.current_tick += 1;

        StepResult::Continue
    }

    /// Starts a loop which calls [`Self::step`] until the world is stopped.
    ///
    /// # Panics
    /// Will panic if a internal lock cannot be acquired.
    pub fn start_loop(&self)
    {
        while let StepResult::Continue = self.step() {}
    }

    #[cfg(feature = "vizoxide")]
    pub fn create_vizoxide_archetype_graph(
        &self,
        name: impl AsRef<str>,
    ) -> Result<vizoxide::Graph, vizoxide::GraphvizError>
    {
        use std::borrow::Cow;

        use crate::component::storage::{
            VizoxideArchetypeGraphEdgeKind,
            VizoxideArchetypeGraphParams,
        };

        let component_storage_lock = self
            .data
            .component_storage
            .read_nonblock()
            .expect("Failed to acquire read-only component storage lock");

        component_storage_lock.create_vizoxide_archetype_graph(
            name,
            VizoxideArchetypeGraphParams {
                create_node_name: |archetype, _| {
                    Cow::Owned(format!(
                        "[{}]",
                        archetype
                            .component_ids_sorted()
                            .into_iter()
                            .map(|comp_id| comp_id.id().to_string())
                            .collect::<Vec<_>>()
                            .join(", ")
                    ))
                },
                create_node_cb: |_archetype, archetype_metadata, node_builder| {
                    if archetype_metadata.is_imaginary {
                        return node_builder.attribute("shape", "ellipse");
                    }

                    node_builder.attribute("shape", "box")
                },
                create_edge_cb: |_, _, edge_kind, edge_builder| {
                    edge_builder.attribute(
                        "color",
                        match edge_kind {
                            VizoxideArchetypeGraphEdgeKind::Add => "green",
                            VizoxideArchetypeGraphEdgeKind::Remove => "red",
                        },
                    )
                },
            },
        )
    }

    fn query_and_run_systems(&self, phase_euid: Uid)
    {
        let system_comps_query =
            self.query::<(&SystemComponent, &Relationship<DependsOn, Phase>), ()>();

        let system_iter = system_comps_query.iter().filter(|(_, phase_rel)| {
            phase_rel
                .target_uids()
                .any(|target_uid| target_uid == phase_euid)
        });

        for (system_component, _) in system_iter {
            // SAFETY: The world lives long enough
            unsafe {
                system_component.system.run(self);
            }
        }
    }

    fn perform_child_phases(&self, parent_phase_euid: Uid)
    {
        let phase_query = self.query::<(&Phase, &Relationship<ChildOf, Phase>), ()>();

        for (child_phase_euid, (_, phase_rel)) in phase_query.iter_with_euids() {
            if !phase_rel
                .target_uids()
                .any(|phase_euid| phase_euid == parent_phase_euid)
            {
                continue;
            }

            self.query_and_run_systems(child_phase_euid);
            self.perform_child_phases(child_phase_euid);
        }
    }

    fn perform_phases(&self)
    {
        let phase_query =
            self.query::<(&Phase,), (Without<Relationship<ChildOf, Phase>>,)>();

        for (phase_euid, (_,)) in phase_query.iter_with_euids() {
            if phase_euid == *START_PHASE {
                continue;
            }

            self.query_and_run_systems(phase_euid);
            self.perform_child_phases(phase_euid);
        }
    }

    #[tracing::instrument(skip_all)]
    fn perform_queued_actions(&self)
    {
        let mut active_action_queue = match *self.data.action_queue.active_queue.borrow()
        {
            ActiveActionQueue::A => &self.data.action_queue.queue_a,
            ActiveActionQueue::B => &self.data.action_queue.queue_b,
        }
        .write_nonblock()
        .unwrap_or_else(|err| {
            panic!(
                "Failed to take read-write action queue lock {:?}: {err}",
                self.data.action_queue.active_queue
            );
        });

        let mut has_swapped_active_queue = false;

        for action in active_action_queue.drain(..) {
            match action {
                Action::Spawn(components, component_added_event_ids) => {
                    {
                        let mut component_storage_lock = self.lock_component_storage_rw();

                        let new_entity_uid = Uid::new_unique(UidKind::Entity);

                        if let Err(err) =
                            component_storage_lock.create_entity(new_entity_uid)
                        {
                            tracing::warn!("Failed to create entity: {err}");
                            continue;
                        };

                        Self::add_entity_components(
                            new_entity_uid,
                            components,
                            &mut component_storage_lock,
                        );
                    }

                    if !has_swapped_active_queue {
                        self.swap_event_queue(&mut has_swapped_active_queue);
                    }

                    for comp_added_event_id in component_added_event_ids.ids {
                        self.emit_event_by_id(comp_added_event_id);
                    }
                }
                Action::Despawn(entity_uid) => {
                    self.despawn_entity(entity_uid, &mut has_swapped_active_queue);
                }
                Action::AddComponents(
                    entity_uid,
                    components,
                    component_added_event_ids,
                ) => {
                    {
                        let mut component_storage_lock = self.lock_component_storage_rw();

                        Self::add_entity_components(
                            entity_uid,
                            components,
                            &mut component_storage_lock,
                        );
                    }

                    if !has_swapped_active_queue {
                        self.swap_event_queue(&mut has_swapped_active_queue);
                    }

                    // TODO: Fix that events are emitted for components that haven't been
                    // added because a error occurred (for example, the entity already has
                    // the component)
                    for comp_added_event_id in component_added_event_ids.ids {
                        self.emit_event_by_id(comp_added_event_id);
                    }
                }
                Action::RemoveComponents(
                    entity_uid,
                    components_metadata,
                    component_removed_event_ids,
                ) => {
                    {
                        let mut component_storage_lock = self.lock_component_storage_rw();

                        Self::remove_entity_components(
                            entity_uid,
                            components_metadata
                                .iter()
                                .map(|comp_metadata| comp_metadata.id),
                            &mut component_storage_lock,
                        );
                    }

                    if !has_swapped_active_queue {
                        self.swap_event_queue(&mut has_swapped_active_queue);
                    }

                    // TODO: Fix that events are emitted for components that haven't been
                    // removed because a error occurred (for example, the entity does not
                    // have the component)
                    for comp_removed_event_id in component_removed_event_ids.ids {
                        self.emit_event_by_id(comp_removed_event_id);
                    }
                }
                Action::Stop => {
                    self.stop.store(true, Ordering::Relaxed);
                }
            }
        }
    }

    #[tracing::instrument(skip_all)]
    fn despawn_entity(&self, entity_uid: Uid, has_swapped_active_queue: &mut bool)
    {
        let mut component_storage_lock = self.lock_component_storage_rw();

        let removed_entity = match component_storage_lock.remove_entity(entity_uid) {
            Ok(components) => components,
            Err(err) => {
                tracing::error!("Failed to despawn entity: {err}");
                return;
            }
        };

        let component_removed_event_uids = removed_entity
            .components()
            .iter()
            .map(|component| {
                component
                    .component()
                    .read_nonblock()
                    .unwrap_or_else(|_| {
                        panic!(
                            "Failed to acquire read-only {} component lock",
                            component.name()
                        )
                    })
                    .get_event_uid(ComponentEventKind::Removed)
            })
            .collect::<Vec<_>>();

        drop(component_storage_lock);

        if !*has_swapped_active_queue {
            self.swap_event_queue(has_swapped_active_queue);
        }

        for comp_removed_event_id in component_removed_event_uids {
            self.emit_event_by_id(comp_removed_event_id);
        }
    }

    fn add_entity_components(
        entity_uid: Uid,
        components: impl IntoIterator<Item = (Uid, Box<dyn Component>)>,
        component_storage: &mut ComponentStorage,
    )
    {
        for (component_id, component) in components {
            if let Err(err) = component_storage.add_entity_component(
                entity_uid,
                (component_id, component.name(), component),
            ) {
                tracing::error!("Failed to add component to entity: {err}");
            }
        }
    }

    fn remove_entity_components(
        entity_uid: Uid,
        component_ids: impl IntoIterator<Item = Uid>,
        component_storage: &mut ComponentStorage,
    )
    {
        for component_id in component_ids {
            if let Err(err) =
                component_storage.remove_entity_component(entity_uid, component_id)
            {
                tracing::error!("Failed to remove component to entity: {err}");
            }
        }
    }

    fn emit_event_by_id(&self, event_id: Uid)
    {
        let query = self.flexible_query(
            QueryTerms::<2>::builder()
                .with_required([SystemComponent::id(), event_id])
                .build(),
        );

        for (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) {
            unsafe {
                system.system.run(self);
            }
        }
    }

    fn swap_event_queue(&self, has_swapped_active_queue: &mut bool)
    {
        let mut active_queue = self.data.action_queue.active_queue.borrow_mut();

        *active_queue = match *active_queue {
            ActiveActionQueue::A => ActiveActionQueue::B,
            ActiveActionQueue::B => ActiveActionQueue::A,
        };

        *has_swapped_active_queue = true;
    }

    fn lock_component_storage_rw(&self) -> WriteGuard<'_, ComponentStorage>
    {
        self.data
            .component_storage
            .write_nonblock()
            .expect("Failed to acquire read-write component storage lock")
    }
}

impl Default for World
{
    fn default() -> Self
    {
        Self::new()
    }
}

/// The result of calling [`World::step`].
pub enum StepResult
{
    /// Another step can be made.
    Continue,

    /// The world have been stopped so no step can be made again.
    Stop,
}

#[derive(Debug)]
pub struct WorldData
{
    component_storage: Arc<Lock<ComponentStorage>>,
    sole_storage: SoleStorage,
    action_queue: Arc<ActionQueue>,
}

impl Default for WorldData
{
    fn default() -> Self
    {
        Self {
            component_storage: Arc::new(Lock::new(
                ComponentStorage::default(),
                type_name::<ComponentStorage>(),
            )),
            sole_storage: SoleStorage::default(),
            action_queue: Arc::new(ActionQueue::default()),
        }
    }
}

#[derive(Debug)]
pub struct EntityComponentRef<'a>
{
    comp: &'a ArchetypeEntityComponent,
}

impl<'a> EntityComponentRef<'a>
{
    fn component(&self) -> &'a Lock<Box<dyn Component>>
    {
        self.comp.component()
    }

    fn new(comp: &'a ArchetypeEntityComponent) -> Self
    {
        Self { comp }
    }
}

#[derive(Debug, Default, Clone, Copy)]
enum ActiveActionQueue
{
    #[default]
    A,
    B,
}

#[derive(Debug)]
struct ActionQueue
{
    queue_a: Lock<Vec<Action>>,
    queue_b: Lock<Vec<Action>>,
    active_queue: RefCell<ActiveActionQueue>,
}

impl ActionQueue
{
    fn push(&self, action: Action)
    {
        match *self.active_queue.borrow() {
            ActiveActionQueue::A => self
                .queue_a
                .write_nonblock()
                .expect("Failed to aquire read-write action queue A lock")
                .push(action),
            ActiveActionQueue::B => self
                .queue_b
                .write_nonblock()
                .expect("Failed to aquire read-write action queue A lock")
                .push(action),
        }
    }
}

impl Default for ActionQueue
{
    fn default() -> Self
    {
        Self {
            queue_a: Lock::new(Vec::new(), type_name::<Vec<Action>>()),
            queue_b: Lock::new(Vec::new(), type_name::<Vec<Action>>()),
            active_queue: RefCell::new(ActiveActionQueue::default()),
        }
    }
}

#[derive(Debug, thiserror::Error)]
#[error("Sole {0} already exists")]
pub struct SoleAlreadyExistsError(pub &'static str);

#[derive(Debug)]
struct StoredSole
{
    sole: Arc<Lock<Box<dyn Sole>>>,
    drop_last: bool,
}

#[derive(Debug, Default)]
struct SoleStorage
{
    storage: HashMap<TypeId, ManuallyDrop<StoredSole>>,
}

impl SoleStorage
{
    fn get<SoleT: Sole>(&self) -> Option<&Arc<Lock<Box<dyn Sole>>>>
    {
        self.storage
            .get(&TypeId::of::<SoleT>())
            .map(|sole| &sole.sole)
    }

    fn insert<SoleT: Sole>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
    {
        let sole_type_id = TypeId::of::<SoleT>();

        if self.storage.contains_key(&sole_type_id) {
            return Err(SoleAlreadyExistsError(type_name::<SoleT>()));
        }

        let drop_last = sole.drop_last();

        // TODO: Reconsider this maybe?
        #[allow(clippy::arc_with_non_send_sync)]
        self.storage.insert(
            sole_type_id,
            ManuallyDrop::new(StoredSole {
                sole: Arc::new(Lock::new(Box::new(sole), type_name::<SoleT>())),
                drop_last,
            }),
        );

        Ok(())
    }
}

impl Drop for SoleStorage
{
    fn drop(&mut self)
    {
        let mut soles_to_drop_last = Vec::new();

        for sole in self.storage.values_mut() {
            if sole.drop_last {
                soles_to_drop_last.push(sole);
                continue;
            }

            unsafe {
                ManuallyDrop::drop(sole);
            }
        }

        for sole in &mut soles_to_drop_last {
            unsafe {
                ManuallyDrop::drop(sole);
            }
        }
    }
}