/*
    Appellation: observer <module>
    Contrib: @FL03
*/
use crate::core::rstmt::prelude::{HarmonicFunction, LPR, PitchMod, Triad, Triads};
use crate::ctx::ActorContext;
use crate::drivers::RawDriver;
use crate::mem::TopoLedger;
use crate::{Actor, VNode};
use rshyper::EdgeId;
use scsys::Id;

use num_traits::{Float, FromPrimitive, NumAssign};

/// a type alias for a [`VecDeque`](alloc::collections::VecDeque) of observations, where each observation
/// consists of a leading pitch region (LPR), a triad, and its context triads.
pub(crate) type Observations = alloc::collections::VecDeque<(LPR, Triads, Triads)>;
/// A type alias for a mapping of triads to their harmonic functions.
pub(crate) type TonalRegions = std::collections::HashMap<[usize; 3], String>;

/// Observer actors monitor the system without directly intervening.
/// They collect data, analyze patterns, and build context but do not
/// initiate transformations.
#[derive(Clone, Debug)]
pub struct Observer<T = f32> {
    pub(crate) id: Id,
    /// Threshold for accepting transformations (0.0 - 1.0)
    pub(crate) threshold: T,
    /// Recent observations of transformations and their context
    pub(crate) observations: Observations,
    /// Known tonal regions mapped by their center triads
    pub(crate) tonal_regions: TonalRegions,
    /// Initialized flag
    pub(crate) initialized: bool,
}

impl<T> Observer<T> {
    /// Create a new observer
    pub fn new() -> Self
    where
        T: FromPrimitive,
    {
        let id: Id;
        #[cfg(feature = "rand")]
        {
            id = Id::<u128>::random().map(|id| id as usize);
        }
        #[cfg(not(feature = "rand"))]
        {
            id = Id::atomic();
        }
        Observer {
            id,
            threshold: T::from_f32(0.95).unwrap(),
            observations: Observations::new(),
            tonal_regions: TonalRegions::new(),
            initialized: false,
        }
    }
    /// returns a copy of the observer's ID
    pub const fn id(&self) -> Id {
        self.id
    }
    /// returns true if the observer is initialized
    pub const fn is_initialized(&self) -> bool {
        self.initialized
    }
    /// returns true if the observer is not initialized
    pub const fn is_uninitialized(&self) -> bool {
        !self.initialized
    }
    /// returns a reference to the observation threshold
    pub const fn threshold(&self) -> &T {
        &self.threshold
    }
    /// returns a mutable reference to the observation threshold
    pub const fn threshold_mut(&mut self) -> &mut T {
        &mut self.threshold
    }
    /// returns an immutable reference to the observations
    pub const fn observations(&self) -> &Observations {
        &self.observations
    }
    /// returns a mutable reference to the observations
    pub const fn observations_mut(&mut self) -> &mut Observations {
        &mut self.observations
    }
    /// returns an immutable reference to the tonal regions
    pub const fn tonal_regions(&self) -> &TonalRegions {
        &self.tonal_regions
    }
    /// returns a mutable reference to the tonal regions
    pub const fn tonal_regions_mut(&mut self) -> &mut TonalRegions {
        &mut self.tonal_regions
    }
    /// update the current id and return a mutable reference to the observer
    pub fn set_id(&mut self, id: Id) -> &mut Self {
        self.id = id;
        self
    }
    /// update the current threshold and return a mutable reference to the observer
    pub fn set_threshold(&mut self, threshold: T) -> &mut Self
    where
        T: Float,
    {
        self.threshold = threshold.max(T::zero()).min(T::one());
        self
    }
    /// update the current observations and return a mutable reference to the observer
    pub fn set_observations(&mut self, observations: Observations) -> &mut Self {
        self.observations = observations;
        self
    }
    /// update the current tonal regions and return a mutable reference to the observer
    pub fn set_tonal_regions(&mut self, tonal_regions: TonalRegions) -> &mut Self {
        self.tonal_regions = tonal_regions;
        self
    }
    /// consumes the current instance to toggle the initialized flag
    pub fn initialized(self) -> Self {
        Self {
            initialized: !self.initialized,
            ..self
        }
    }
    /// consumes the current instance to create another with the given ID
    pub fn with_id(self, id: Id) -> Self {
        Self { id, ..self }
    }
    /// consumes the current instance to create another with the given observations
    pub fn with_observations(self, observations: Observations) -> Self {
        Self {
            observations,
            ..self
        }
    }
    /// consumes the current instance to create another with the given threshold
    pub fn with_threshold(self, threshold: T) -> Self
    where
        T: Float,
    {
        Self {
            threshold: threshold.max(T::zero()).min(T::one()),
            ..self
        }
    }
    /// consumes the current instance to create another with the given tonal regions
    pub fn with_tonal_regions(self, tonal_regions: TonalRegions) -> Self {
        Self {
            tonal_regions,
            ..self
        }
    }
    /// set the initialized flag to true
    pub fn initialize(self) -> Self {
        Self {
            initialized: true,
            ..self
        }
    }
    /// Get the number of transformations observed
    pub fn observation_count(&self) -> usize {
        self.observations.len()
    }

    /// Analyze recent transformations for patterns
    pub fn analyze_patterns(&self, memory: &TopoLedger<T>) -> Vec<(Vec<LPR>, T)>
    where
        T: Float + FromPrimitive,
    {
        let mut patterns = Vec::new();

        // Extract recent LPRs
        let lpr_sequence: Vec<_> = self.observations.iter().map(|(lpr, _, _)| *lpr).collect();

        if lpr_sequence.len() < 3 {
            return patterns;
        }

        // Look for repeated subsequences
        for window_size in 2..=lpr_sequence.len() / 2 {
            for start in 0..=lpr_sequence.len() - window_size {
                let pattern = lpr_sequence[start..start + window_size].to_vec();
                let mut count = 0;

                // Count occurrences in memory
                for mem_pattern in memory.patterns() {
                    if mem_pattern.sequence_len() >= pattern.len() {
                        for i in 0..=mem_pattern.sequence_len() - pattern.len() {
                            let mut matches = true;
                            for j in 0..pattern.len() {
                                // Convert usize back to LPR for comparison
                                let mem_lpr = match mem_pattern[i + j] {
                                    0 => LPR::Leading,
                                    1 => LPR::Parallel,
                                    2 => LPR::Relative,
                                    _ => continue,
                                };

                                if mem_lpr != pattern[j] {
                                    matches = false;
                                    break;
                                }
                            }

                            if matches {
                                count += 1;
                            }
                        }
                    }
                }

                // If pattern occurs multiple times
                if count > 1 {
                    let importance = T::from_usize(count).unwrap()
                        / T::from_usize(memory.patterns().len()).unwrap();
                    if &importance >= self.threshold() {
                        patterns.push((pattern, importance));
                    }
                }
            }
        }

        // Sort by importance
        patterns.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(core::cmp::Ordering::Equal));
        patterns
    }
    /// Analyze a pattern and derive semantic meaning
    fn analyze_pattern(&self, pattern: &[usize]) -> Vec<String> {
        if pattern.is_empty() {
            return Vec::new();
        }

        let mut meanings = Vec::new();

        // Common cadential patterns
        if pattern == &[2, 1] {
            // Relative -> Parallel
            meanings.push("Cadential".to_string());
        } else if pattern == &[0, 1] {
            // Leading -> Parallel
            meanings.push("Deceptive".to_string());
        } else if pattern == &[0, 0] {
            // Leading -> Leading
            meanings.push("Sequential".to_string());
        } else if pattern == &[1, 1, 1] {
            // Parallel -> Parallel -> Parallel
            meanings.push("Modal Mixture".to_string());
        } else if pattern == &[2, 2] {
            // Relative -> Relative
            meanings.push("Common-tone Modulation".to_string());
        }

        // Pattern structure analysis
        let mut has_symmetry = false;
        if pattern.len() >= 4 {
            // Check for symmetrical patterns (palindromes)
            let mut is_palindrome = true;
            for i in 0..pattern.len() / 2 {
                if pattern[i] != pattern[pattern.len() - 1 - i] {
                    is_palindrome = false;
                    break;
                }
            }
            has_symmetry = is_palindrome;
        }

        if has_symmetry {
            meanings.push("Symmetrical".to_string());
        }

        // Check for cycles
        if pattern.len() >= 3 {
            'cycle_check: for cycle_len in 1..=pattern.len() / 2 {
                if pattern.len() % cycle_len != 0 {
                    continue;
                }

                let mut is_cycle = true;
                for i in 0..pattern.len() - cycle_len {
                    if pattern[i] != pattern[i + cycle_len] {
                        is_cycle = false;
                        break;
                    }
                }

                if is_cycle {
                    meanings.push(format!("Cyclic-{}", cycle_len));
                    break 'cycle_check;
                }
            }
        }

        // Analyze transformation distribution
        let mut counts = [0, 0, 0]; // L, P, R counts
        for &p in pattern {
            if p < 3 {
                counts[p] += 1;
            }
        }

        // Characterize by dominant transformation type
        let total = pattern.len();
        if counts[0] > total / 2 {
            meanings.push("Leading-dominated".to_string());
        } else if counts[1] > total / 2 {
            meanings.push("Parallel-dominated".to_string());
        } else if counts[2] > total / 2 {
            meanings.push("Relative-dominated".to_string());
        } else if counts[0] == counts[1] && counts[1] == counts[2] && counts[0] > 0 {
            meanings.push("Balanced".to_string());
        }

        meanings
    }

    /// Identify tonal region containing a triad
    fn identify_tonal_region(&self, triad: &Triad) -> Option<String> {
        // Simple version - just check if this exact triad is a region center
        self.tonal_regions().get(&triad.notes()).cloned()
    }

    /// Analyze critical points in relation to triad
    pub fn analyze_critical_points<D>(&self, vnode: &VNode<D>) -> Vec<(String, T)>
    where
        D: RawDriver<Triad>,
        T: Float + FromPrimitive,
    {
        let mut analysis = Vec::new();

        // Get current triad and critical points
        let triad = vnode.headspace();
        let critical_points = vnode.critical_points();

        for (name, point) in critical_points {
            // Calculate distance from current triad root to critical point
            // using pitch class distance
            let root = triad.root();
            let distance = (((*point as isize) - (root as isize)).abs().pmod()) as usize;

            // Normalize distance (0.0 = closest, 1.0 = furthest)
            let normalized_distance = T::from_usize(distance).unwrap() / T::from_usize(6).unwrap(); // 6 is max distance in pitch class space

            // Create relationship descriptor
            let relationship = match distance {
                0 => "At",
                1 => "Adjacent to",
                2 => "Near",
                _ => "Distant from",
            };

            analysis.push((
                format!("{} {}", relationship, name),
                T::one() - normalized_distance,
            ));
        }

        analysis
    }
}

impl<D, T> Actor<D, T> for Observer<T>
where
    D: RawDriver<Triad>,
    T: core::iter::Sum + Float + FromPrimitive + NumAssign,
{
    fn kind(&self) -> &'static str {
        super::OperatorKind::Observer.as_ref()
    }

    fn initialize(&mut self) -> crate::Result<()> {
        if !self.initialized {
            self.observations.clear();
            self.initialized = true;
        }
        Ok(())
    }

    fn process_transform(
        &mut self,
        transform: LPR,
        plant: &mut D,
        _memory: &mut TopoLedger<T>,
    ) -> crate::Result<bool> {
        // Record the observation with from and to triad classes
        let from_class = plant.headspace().class();
        // Apply transform temporarily to get resulting class
        let to_triad = plant.headspace().transform(transform);
        let to_class = to_triad.class();

        // Store the observation
        self.observations
            .push_back((transform, from_class, to_class));

        // Keep observation queue from growing too large
        if self.observation_count() > 50 {
            self.observations_mut().pop_front();
        }

        // Observer doesn't modify transformations, just watches
        Ok(true) // Always allow transformation
    }

    fn process_message(
        &mut self,
        _source: EdgeId,
        _message: &[u8],
        _memory: &mut TopoLedger<T>,
    ) -> crate::Result<()> {
        // Observers don't process messages, they just observe
        Ok(())
    }

    fn on_activate(&mut self, _plant: &D, _memory: &mut TopoLedger<T>) -> crate::Result<()> {
        self.observations.clear();
        Ok(())
    }

    fn on_deactivate(&mut self, _plant: &D, memory: &mut TopoLedger<T>) -> crate::Result<()> {
        // Before deactivation, record patterns
        for i in 2..=4 {
            // Look for patterns of length 2-4
            if self.observation_count() >= i {
                // Get the most recent i transformations
                let recent: Vec<usize> = self
                    .observations
                    .iter()
                    .rev()
                    .take(i)
                    .map(|(lpr, _, _)| match lpr {
                        LPR::Leading => 0,
                        LPR::Parallel => 1,
                        LPR::Relative => 2,
                    })
                    .collect();

                // Record the pattern with medium importance
                memory.record_pattern(&recent);
            }
        }

        Ok(())
    }

    fn resource_requirements(&self) -> (usize, usize) {
        // Observer uses minimal resources (memory, compute)
        (50, 10)
    }

    fn allows_pattern_sharing(&self) -> bool {
        true // Observer allows pattern sharing
    }

    fn contextualize(&self, plant: &D, memory: &TopoLedger<T>) -> crate::Result<ActorContext<T>> {
        let triad = plant.headspace();

        // Calculate stability based on memory relationships
        let stability = if memory.count_features() > 0 {
            memory.get_stability_for(&triad.notes())
        } else {
            // Default stability for major/minor triads
            match triad.class() {
                crate::nrt::Triads::Major => T::from_f32(0.8).unwrap(),
                crate::nrt::Triads::Minor => T::from_f32(0.7).unwrap(),
                _ => T::from_f32(0.5).unwrap(),
            }
        };

        // Determine harmonic function
        // Using C as tonic reference
        let harmonic_function = match triad.root().pmod() {
            0 => Some(HarmonicFunction::Tonic),        // C
            7 => Some(HarmonicFunction::Dominant),     // G
            5 => Some(HarmonicFunction::Subdominant),  // F
            9 => Some(HarmonicFunction::Submediant),   // A
            2 => Some(HarmonicFunction::Supertonic),   // D
            11 => Some(HarmonicFunction::LeadingTone), // B
            4 => Some(HarmonicFunction::Mediant),      // E
            _ => None,
        };

        // Generate semantic tags
        let mut semantic_tags = Vec::new();

        // Add tonal region tag if recognized
        if let Some(region) = self.identify_tonal_region(triad) {
            semantic_tags.push(format!("Region:{}", region));
        }

        // Try to find patterns in recent transformations
        if let Some(recent_pattern) = memory.get_recent_pattern(5) {
            // Add pattern semantic analysis
            let pattern_meanings = self.analyze_pattern(&recent_pattern);
            semantic_tags.extend(pattern_meanings);
        }

        // Add descriptor based on stability
        if stability > T::from_f32(0.8).unwrap() {
            semantic_tags.push("Stable".to_string());
        } else if stability < T::from_f32(0.4).unwrap() {
            semantic_tags.push("Unstable".to_string());
        }

        // Create context
        Ok(ActorContext {
            harmonic_function,
            metric_position: None, // Observer doesn't track metrics
            semantic_tags,
            stability,
        })
    }
}

impl<T> Default for Observer<T>
where
    T: Default + FromPrimitive,
{
    fn default() -> Self {
        Observer::new()
    }
}

impl<T> PartialEq<Observer<T>> for Observer<T> {
    fn eq(&self, other: &Observer<T>) -> bool {
        self.id() == other.id()
    }
}

impl<T> PartialEq<Observer<T>> for &Observer<T> {
    fn eq(&self, other: &Observer<T>) -> bool {
        self.id() == other.id()
    }
}

impl<T> PartialEq<Observer<T>> for &mut Observer<T> {
    fn eq(&self, other: &Observer<T>) -> bool {
        self.id() == other.id()
    }
}

impl<'a, T> PartialEq<&'a Observer<T>> for Observer<T> {
    fn eq(&self, other: &&'a Observer<T>) -> bool {
        self.id() == other.id()
    }
}

impl<'a, T> PartialEq<&'a mut Observer<T>> for Observer<T> {
    fn eq(&self, other: &&'a mut Observer<T>) -> bool {
        self.id() == other.id()
    }
}

impl<T> Eq for Observer<T> {}

impl<T> core::hash::Hash for Observer<T> {
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        self.id().hash(state);
        // self.threshold().hash(state);
        self.observations().hash(state);
        self.is_initialized().hash(state);
    }
}
