//! Multi-layer corpus resolver with conflict resolution.
//!
//! The resolver manages priority-ordered corpus layers:
//! 1. Manual overrides (highest authority)
//! 2. Trust Packs (curated by experts)
//! 3. Community promoted patterns (emergent)

use std::collections::HashMap;

use ed25519_dalek::SigningKey;
use stemedb_core::types::Assertion;
use tracing::{info, instrument};

use super::CorpusBuilder;
use crate::config::CorpusConfig;
use crate::AphoriaError;

/// Multi-layer corpus resolver with priority-based conflict resolution.
///
/// Corpus layers are evaluated in priority order. When multiple layers
/// provide assertions for the same (subject, predicate) pair, the
/// highest-priority layer wins.
pub struct CorpusResolver {
    /// Ordered list of corpus builders (first = highest priority).
    layers: Vec<CorpusLayer>,
}

/// A corpus layer with metadata.
struct CorpusLayer {
    /// The corpus builder.
    builder: Box<dyn CorpusBuilder>,

    /// Priority level (lower = higher priority).
    priority: u8,

    /// Human-readable description of this layer's purpose.
    description: String,
}

impl CorpusResolver {
    /// Create a new empty resolver.
    pub fn new() -> Self {
        Self { layers: Vec::new() }
    }

    /// Create a resolver with default layers based on config.
    ///
    /// Layer priority (lower = higher):
    /// - Priority 0: Manual overrides
    /// - Priority 1: Trust Packs
    /// - Priority 2: Community promoted patterns
    pub fn with_defaults(_config: &CorpusConfig) -> Self {
        // NOTE: Manual overrides and Trust Pack builders not yet implemented
        // They will be added in future phases when needed

        // For now, just create empty resolver
        // Layers will be added incrementally:
        // - Week 1-2: Shadow mode (no layers yet)
        // - Week 3-4: Community layer (opt-in)
        // - Week 5: Make community default

        Self::new()
    }

    /// Register a corpus builder with a specific priority.
    ///
    /// # Arguments
    ///
    /// * `builder` - The corpus builder to register
    /// * `priority` - Priority level (lower = higher priority)
    /// * `description` - Human-readable description
    pub fn register(
        &mut self,
        builder: Box<dyn CorpusBuilder>,
        priority: u8,
        description: impl Into<String>,
    ) {
        let layer = CorpusLayer { builder, priority, description: description.into() };

        // Insert in priority order (lowest priority value first)
        let insert_pos =
            self.layers.iter().position(|l| l.priority > priority).unwrap_or(self.layers.len());

        self.layers.insert(insert_pos, layer);
    }

    /// Get registered layer names and priorities.
    pub fn layer_info(&self) -> Vec<(String, u8, String)> {
        self.layers
            .iter()
            .map(|l| (l.builder.name().to_string(), l.priority, l.description.clone()))
            .collect()
    }

    /// Build corpus with conflict resolution (higher priority wins).
    ///
    /// # Conflict Resolution
    ///
    /// When multiple layers provide assertions for the same (subject, predicate):
    /// 1. Lower priority assertions are built first
    /// 2. Higher priority assertions overwrite them
    /// 3. Result: highest-priority assertion wins
    ///
    /// # Arguments
    ///
    /// * `signing_key` - Ed25519 key for signing assertions
    /// * `timestamp` - Unix timestamp for assertion creation
    /// * `config` - Corpus configuration
    ///
    /// # Returns
    ///
    /// A vector of resolved assertions (one per subject+predicate, highest priority).
    #[instrument(skip(self, signing_key, config), fields(layers = self.layers.len()))]
    pub async fn build(
        &self,
        signing_key: &SigningKey,
        timestamp: u64,
        config: &CorpusConfig,
    ) -> Result<Vec<Assertion>, AphoriaError> {
        // Use HashMap for conflict resolution: (subject, predicate) → assertion
        let mut assertions_by_key: HashMap<(String, String), (Assertion, u8)> = HashMap::new();

        // Iterate in REVERSE order: lowest priority first, higher overwrites
        for layer in self.layers.iter().rev() {
            info!(
                builder = layer.builder.name(),
                priority = layer.priority,
                description = %layer.description,
                "Building corpus layer"
            );

            let layer_assertions = layer.builder.build(signing_key, timestamp, config)?;

            info!(
                builder = layer.builder.name(),
                assertions = layer_assertions.len(),
                "Layer built"
            );

            for assertion in layer_assertions {
                let key = (assertion.subject.clone(), assertion.predicate.clone());

                // Check if we already have this key
                if let Some((_existing, existing_priority)) = assertions_by_key.get(&key) {
                    if layer.priority < *existing_priority {
                        // Higher priority (lower number) - overwrite
                        info!(
                            subject = %assertion.subject,
                            predicate = %assertion.predicate,
                            old_priority = existing_priority,
                            new_priority = layer.priority,
                            "Overwriting assertion with higher priority"
                        );
                        assertions_by_key.insert(key, (assertion, layer.priority));
                    }
                    // else: existing has higher priority, keep it
                } else {
                    // New key, insert it
                    assertions_by_key.insert(key, (assertion, layer.priority));
                }
            }
        }

        // Extract just the assertions (discard priorities)
        Ok(assertions_by_key.into_values().map(|(assertion, _priority)| assertion).collect())
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    use crate::bridge::generate_signing_key;
    use stemedb_core::types::{ObjectValue, SourceClass};

    // Mock corpus builder for testing
    struct MockCorpusBuilder {
        name: String,
        assertions: Vec<(String, String, ObjectValue)>, // (subject, predicate, value)
    }

    impl MockCorpusBuilder {
        fn new(name: impl Into<String>, assertions: Vec<(String, String, ObjectValue)>) -> Self {
            Self { name: name.into(), assertions }
        }
    }

    impl CorpusBuilder for MockCorpusBuilder {
        fn name(&self) -> &str {
            &self.name
        }

        fn scheme(&self) -> &str {
            "mock"
        }

        fn default_tier(&self) -> u8 {
            2
        }

        fn build(
            &self,
            signing_key: &SigningKey,
            timestamp: u64,
            _config: &CorpusConfig,
        ) -> Result<Vec<Assertion>, AphoriaError> {
            use crate::episteme::create_authoritative_assertion;

            Ok(self
                .assertions
                .iter()
                .map(|(subj, pred, val)| {
                    create_authoritative_assertion(
                        signing_key,
                        subj,
                        pred,
                        val.clone(),
                        SourceClass::Observational,
                        "Mock assertion",
                        timestamp,
                    )
                })
                .collect())
        }
    }

    #[tokio::test]
    async fn test_empty_resolver() {
        let resolver = CorpusResolver::new();
        let key = generate_signing_key();
        let config = CorpusConfig::default();

        let assertions = resolver.build(&key, 1706832000, &config).await.expect("build");

        assert_eq!(assertions.len(), 0);
    }

    #[tokio::test]
    async fn test_single_layer() {
        let mut resolver = CorpusResolver::new();

        let builder = MockCorpusBuilder::new(
            "TestBuilder",
            vec![
                (
                    "test://subject1".to_string(),
                    "predicate1".to_string(),
                    ObjectValue::Boolean(true),
                ),
                (
                    "test://subject2".to_string(),
                    "predicate2".to_string(),
                    ObjectValue::Text("value".to_string()),
                ),
            ],
        );

        resolver.register(Box::new(builder), 0, "Test layer");

        let key = generate_signing_key();
        let config = CorpusConfig::default();

        let assertions = resolver.build(&key, 1706832000, &config).await.expect("build");

        assert_eq!(assertions.len(), 2);
    }

    #[tokio::test]
    async fn test_priority_override() {
        let mut resolver = CorpusResolver::new();

        // Low priority builder (priority=2)
        let low_priority = MockCorpusBuilder::new(
            "LowPriority",
            vec![(
                "test://subject".to_string(),
                "predicate".to_string(),
                ObjectValue::Boolean(false),
            )],
        );

        // High priority builder (priority=0)
        let high_priority = MockCorpusBuilder::new(
            "HighPriority",
            vec![(
                "test://subject".to_string(),
                "predicate".to_string(),
                ObjectValue::Boolean(true),
            )],
        );

        resolver.register(Box::new(low_priority), 2, "Low priority");
        resolver.register(Box::new(high_priority), 0, "High priority");

        let key = generate_signing_key();
        let config = CorpusConfig::default();

        let assertions = resolver.build(&key, 1706832000, &config).await.expect("build");

        // Should have 1 assertion (high priority overwrites low)
        assert_eq!(assertions.len(), 1);

        // Value should be from high priority builder (true, not false)
        let assertion = &assertions[0];
        assert_eq!(assertion.object, ObjectValue::Boolean(true));
    }

    #[tokio::test]
    async fn test_multiple_layers_no_conflict() {
        let mut resolver = CorpusResolver::new();

        let builder1 = MockCorpusBuilder::new(
            "Builder1",
            vec![(
                "test://subject1".to_string(),
                "predicate".to_string(),
                ObjectValue::Boolean(true),
            )],
        );

        let builder2 = MockCorpusBuilder::new(
            "Builder2",
            vec![(
                "test://subject2".to_string(),
                "predicate".to_string(),
                ObjectValue::Boolean(false),
            )],
        );

        resolver.register(Box::new(builder1), 0, "Layer 1");
        resolver.register(Box::new(builder2), 1, "Layer 2");

        let key = generate_signing_key();
        let config = CorpusConfig::default();

        let assertions = resolver.build(&key, 1706832000, &config).await.expect("build");

        // Should have 2 assertions (different subjects, no conflict)
        assert_eq!(assertions.len(), 2);
    }

    #[tokio::test]
    async fn test_layer_info() {
        let mut resolver = CorpusResolver::new();

        let builder1 = MockCorpusBuilder::new("Builder1", vec![]);
        let builder2 = MockCorpusBuilder::new("Builder2", vec![]);

        resolver.register(Box::new(builder1), 0, "First layer");
        resolver.register(Box::new(builder2), 1, "Second layer");

        let info = resolver.layer_info();

        assert_eq!(info.len(), 2);
        assert_eq!(info[0].0, "Builder1");
        assert_eq!(info[0].1, 0); // priority
        assert_eq!(info[0].2, "First layer");
        assert_eq!(info[1].0, "Builder2");
        assert_eq!(info[1].1, 1); // priority
    }

    #[tokio::test]
    async fn test_with_defaults_empty() {
        let config = CorpusConfig::default();
        let resolver = CorpusResolver::with_defaults(&config);

        // Should be empty in shadow mode (Week 1-2)
        assert_eq!(resolver.layers.len(), 0);
    }
}
