use anyhow::Result;
use std::path::Path;

use crate::knowledge::KnowledgeEntry;
use crate::types::{
    Agent, ApplicabilityType, Category, ContentType, EntryType, Project, Relationship,
    RelationshipType, Session, SessionType, SourceType,
};

/// Agent context for privacy-aware queries
#[derive(Debug, Clone)]
pub struct AgentContext {
    /// Current agent ID (None = anonymous/public-only access)
    pub agent_id: Option<String>,
    /// Whether to include private entries (requires matching agent_id)
    pub include_private: bool,
}

impl AgentContext {
    /// Public-only access (no private entries visible)
    pub fn public_only() -> Self {
        Self {
            agent_id: None,
            include_private: false,
        }
    }

    /// Agent with full access to their private entries
    pub fn for_agent(id: impl Into<String>) -> Self {
        Self {
            agent_id: Some(id.into()),
            include_private: true,
        }
    }

    /// Agent but only viewing public entries
    pub fn public_for_agent(id: impl Into<String>) -> Self {
        Self {
            agent_id: Some(id.into()),
            include_private: false,
        }
    }
}

/// Filter for resonance-based queries
#[derive(Debug, Clone, Default)]
pub struct KnowledgeFilter {
    pub min_resonance: Option<i32>,
    pub max_resonance: Option<i32>,
    pub categories: Option<Vec<String>>,
}

/// Result of a wake-up cascade query
#[derive(Debug, Clone, serde::Serialize)]
pub struct WakeCascade {
    /// Layer 1: Foundational/transformative, resonance 8+
    pub core: Vec<crate::knowledge::KnowledgeEntry>,
    /// Layer 2: Last N days, sorted by resonance * recency
    pub recent: Vec<crate::knowledge::KnowledgeEntry>,
    /// Layer 3: Anchored to core/recent, resonance 5+
    pub bridges: Vec<crate::knowledge::KnowledgeEntry>,
}

impl WakeCascade {
    pub fn all_ids(&self) -> Vec<String> {
        self.core
            .iter()
            .chain(self.recent.iter())
            .chain(self.bridges.iter())
            .map(|e| e.id.clone())
            .collect()
    }
}

/// Result of an edit_content operation
#[derive(Debug, Clone)]
pub struct EditResult {
    /// Number of replacements made
    pub replacements: usize,
    /// The updated content (for display purposes)
    pub new_content: String,
}

/// Result of a reinforce operation
#[derive(Debug, Clone, serde::Serialize)]
pub struct ReinforcementResult {
    /// Entry ID that was reinforced
    pub id: String,
    /// Previous resonance value
    pub old_resonance: i32,
    /// New resonance value (after increment and cap)
    pub new_resonance: i32,
    /// Amount added (before cap)
    pub amount_added: i32,
    /// Whether the cap was hit
    pub capped: bool,
    /// New last_activated timestamp
    pub last_activated: String,
    /// New activation count
    pub activation_count: i32,
}

/// Abstract interface for knowledge storage backends
pub trait KnowledgeStore {
    // =========================================================================
    // KNOWLEDGE CRUD OPERATIONS
    // =========================================================================

    /// Upsert a knowledge entry (insert or update)
    fn upsert_knowledge(&self, entry: &KnowledgeEntry) -> Result<()>;

    /// Get a knowledge entry by ID
    fn get(&self, id: &str, ctx: &AgentContext) -> Result<Option<KnowledgeEntry>>;

    /// Delete a knowledge entry (respects visibility: agents can only delete entries they can see)
    fn delete(&self, id: &str, ctx: &AgentContext) -> Result<bool>;

    /// Search knowledge entries
    fn search(
        &self,
        query: &str,
        ctx: &AgentContext,
        filter: &KnowledgeFilter,
    ) -> Result<Vec<KnowledgeEntry>>;

    /// Semantic search using vector similarity
    fn semantic_search(
        &self,
        query_embedding: &[f32],
        ctx: &AgentContext,
        filter: &KnowledgeFilter,
        limit: usize,
    ) -> Result<Vec<KnowledgeEntry>>;

    /// List entries by category
    fn list_by_category(
        &self,
        category: &str,
        ctx: &AgentContext,
        filter: &KnowledgeFilter,
    ) -> Result<Vec<KnowledgeEntry>>;

    /// Count entries by category (fast path — single COUNT query, no row hydration).
    /// Avoids the N+1 pattern of `list_by_category(..)?.len()` which fetches every
    /// row's full body plus follow-up queries for tags/applicability per entry.
    fn count_by_category(
        &self,
        category: &str,
        ctx: &AgentContext,
        filter: &KnowledgeFilter,
    ) -> Result<usize>;

    /// List all entries
    fn list_all(&self, ctx: &AgentContext) -> Result<Vec<KnowledgeEntry>>;

    /// Count total entries
    fn count(&self) -> Result<usize>;

    /// Wake-up cascade query (three-layer resonance)
    fn wake_cascade(
        &self,
        ctx: &AgentContext,
        limit: usize,
        min_resonance: Option<i32>,
        days: i64,
    ) -> Result<WakeCascade>;

    /// Update activation counts for loaded blooms, resetting last_activated timestamp.
    /// Use for intentional single-entry access (e.g. `show`, `fact-session`).
    fn update_activations(&self, ids: &[String]) -> Result<()>;

    /// Update only the summary field of a knowledge entry (targeted update, bypasses SCHEMAFULL UPSERT)
    /// Respects visibility: agents can only update summaries on entries they can see.
    /// Returns Ok(false) for entries that don't exist OR that the agent can't see
    /// (to avoid leaking existence of private entries).
    ///
    /// # Arguments
    /// * `id` - Entry ID, with or without "kn-" prefix (normalized internally)
    /// * `summary` - New summary value to set
    /// * `ctx` - Agent context for visibility filtering
    fn update_summary(&self, id: &str, summary: &str, ctx: &AgentContext) -> Result<bool>;

    /// Increment activation_count only — does NOT reset last_activated.
    /// Use for passive bulk surfacing (wake cascade, for-session view) so entries
    /// continue decaying at their normal rate.
    fn increment_activation_count(&self, ids: &[String]) -> Result<()>;

    /// Query recent ephemeral facts with decay computation
    fn query_recent_facts(&self, days: i32) -> Result<Vec<KnowledgeEntry>>;

    /// Query recent facts across ALL resonance types (ephemeral, foundational, transformative, etc.)
    /// with decay computation. Foundational/transformative entries are exempt from decay.
    fn query_recent_facts_all_types(&self, days: i32) -> Result<Vec<KnowledgeEntry>>;

    /// Reinforce a knowledge entry (increment resonance, update last_activated, increment activation_count)
    /// Respects visibility: agents can only reinforce entries they can see.
    /// Returns Ok(None) for entries that don't exist OR that the agent can't see
    /// (to avoid leaking existence of private entries).
    ///
    /// # Arguments
    /// * `id` - Entry ID to reinforce
    /// * `amount` - Amount to increase resonance by
    /// * `cap` - Maximum resonance value (None = no cap)
    /// * `ctx` - Agent context for visibility filtering
    ///
    /// # Returns
    /// Result containing the old/new values and whether cap was hit, or None if not found/not visible
    fn reinforce(
        &self,
        id: &str,
        amount: i32,
        cap: Option<i32>,
        ctx: &AgentContext,
    ) -> Result<Option<ReinforcementResult>>;

    // =========================================================================
    // CONTENT PATCH OPERATIONS
    // =========================================================================

    /// Edit content by finding and replacing text
    ///
    /// Returns an error if:
    /// - Entry not found
    /// - Entry has no body content
    /// - `old_text` is not found in the content
    /// - `old_text` appears multiple times and neither `replace_all` nor `nth` is specified
    ///
    /// # Arguments
    /// * `id` - Entry ID to update
    /// * `ctx` - Agent context for privacy filtering
    /// * `old_text` - Text to find in the content
    /// * `new_text` - Replacement text
    /// * `replace_all` - If true, replace all occurrences
    /// * `nth` - If Some(n), replace only the nth occurrence (1-indexed)
    fn edit_content(
        &self,
        id: &str,
        ctx: &AgentContext,
        old_text: &str,
        new_text: &str,
        replace_all: bool,
        nth: Option<usize>,
    ) -> Result<EditResult>;

    /// Append content to the end of an entry's body
    ///
    /// Adds the new content after the existing content, separated by a newline.
    /// If the entry has no body, the new content becomes the body.
    fn append_content(&self, id: &str, ctx: &AgentContext, content: &str) -> Result<()>;

    /// Prepend content to the start of an entry's body
    ///
    /// Adds the new content before the existing content, separated by a newline.
    /// If the entry has no body, the new content becomes the body.
    fn prepend_content(&self, id: &str, ctx: &AgentContext, content: &str) -> Result<()>;

    // =========================================================================
    // BACKUP OPERATIONS (Issue #206)
    // =========================================================================

    /// Create a pre-mutation backup of entry content
    fn backup_content(
        &self,
        entry: &KnowledgeEntry,
        operation: &str,
        agent: Option<&str>,
    ) -> Result<String>;

    /// List backups for a specific entry, newest first
    fn list_backups(&self, entry_id: &str) -> Result<Vec<crate::types::MemoryBackup>>;

    /// Get the most recent backup for an entry
    fn latest_backup(&self, entry_id: &str) -> Result<Option<crate::types::MemoryBackup>>;

    /// Purge old backups, keeping the most recent `keep` per entry
    fn purge_backups(&self, entry_id: &str, keep: usize) -> Result<()>;

    // =========================================================================
    // TAG OPERATIONS
    // =========================================================================

    /// Get tags for an entry
    fn get_tags_for_entry(&self, entry_id: &str) -> Result<Vec<String>>;

    /// Set tags for an entry (replaces all)
    fn set_tags_for_entry(&self, entry_id: &str, tags: &[String]) -> Result<()>;

    /// List all distinct tags, optionally filtered by category
    fn list_all_tags(&self, category: Option<&str>) -> Result<Vec<String>>;

    // =========================================================================
    // APPLICABILITY OPERATIONS
    // =========================================================================

    /// Get applicability for an entry
    fn get_applicability_for_entry(&self, entry_id: &str) -> Result<Vec<String>>;

    /// Set applicability for an entry (replaces all)
    fn set_applicability_for_entry(&self, entry_id: &str, ids: &[String]) -> Result<()>;

    /// List all applicability types
    fn list_applicability_types(&self) -> Result<Vec<ApplicabilityType>>;

    /// Upsert applicability type
    fn upsert_applicability_type(&self, atype: &ApplicabilityType) -> Result<()>;

    // =========================================================================
    // CATEGORY OPERATIONS
    // =========================================================================

    /// List all categories
    fn list_categories(&self) -> Result<Vec<Category>>;

    /// Get category by ID
    fn get_category(&self, id: &str) -> Result<Option<Category>>;

    /// Upsert category
    fn upsert_category(&self, category: &Category) -> Result<()>;

    /// Delete category (fails if entries use it)
    fn delete_category(&self, id: &str) -> Result<bool>;

    // =========================================================================
    // PROJECT OPERATIONS
    // =========================================================================

    /// List all projects
    fn list_projects(&self, active_only: bool) -> Result<Vec<Project>>;

    /// Get project by ID
    fn get_project(&self, id: &str) -> Result<Option<Project>>;

    /// Upsert project
    fn upsert_project(&self, project: &Project) -> Result<()>;

    /// Get tags for a project
    fn get_tags_for_project(&self, project_id: &str) -> Result<Vec<String>>;

    /// Set tags for a project
    fn set_tags_for_project(&self, project_id: &str, tags: &[String]) -> Result<()>;

    /// Get applicability for a project
    fn get_applicability_for_project(&self, project_id: &str) -> Result<Vec<String>>;

    /// Set applicability for a project
    fn set_applicability_for_project(&self, project_id: &str, ids: &[String]) -> Result<()>;

    // =========================================================================
    // AGENT OPERATIONS
    // =========================================================================

    /// List all agents
    fn list_agents(&self) -> Result<Vec<Agent>>;

    /// Get agent by ID
    fn get_agent(&self, id: &str) -> Result<Option<Agent>>;

    /// Upsert agent
    fn upsert_agent(&self, agent: &Agent) -> Result<()>;

    // =========================================================================
    // RELATIONSHIP OPERATIONS
    // =========================================================================

    /// List relationships for an entry
    fn list_relationships_for_entry(&self, entry_id: &str) -> Result<Vec<Relationship>>;

    /// Add relationship between entries
    fn add_relationship(&self, from: &str, to: &str, rel_type: &str) -> Result<String>;

    /// Delete relationship
    fn delete_relationship(&self, id: &str) -> Result<bool>;

    /// Get facts extracted from a specific session
    fn get_facts_for_session(&self, session_id: &str) -> Result<Vec<String>>;

    /// Get the session a fact was extracted from
    fn get_session_for_fact(&self, fact_id: &str) -> Result<Option<String>>;

    // =========================================================================
    // SESSION OPERATIONS
    // =========================================================================

    /// List sessions
    fn list_sessions(&self, project_id: Option<&str>) -> Result<Vec<Session>>;

    /// Get session by ID
    fn get_session(&self, id: &str) -> Result<Option<Session>>;

    /// Upsert session
    fn upsert_session(&self, session: &Session) -> Result<()>;

    // =========================================================================
    // TYPE LOOKUP OPERATIONS
    // =========================================================================

    /// List all source types
    fn list_source_types(&self) -> Result<Vec<SourceType>>;

    /// List all entry types
    fn list_entry_types(&self) -> Result<Vec<EntryType>>;

    /// List all content types
    fn list_content_types(&self) -> Result<Vec<ContentType>>;

    /// List all session types
    fn list_session_types(&self) -> Result<Vec<SessionType>>;

    /// List all relationship types
    fn list_relationship_types(&self) -> Result<Vec<RelationshipType>>;

    // =========================================================================
    // WAKE SESSION OPERATIONS (server-side ritual state)
    // =========================================================================

    /// Create a new wake session record, return the session_id
    fn create_wake_session(&self, session: &crate::wake_token::WakeSession) -> Result<String>;

    /// Get a wake session by ID
    fn get_wake_session(&self, session_id: &str) -> Result<Option<crate::wake_token::WakeSession>>;

    /// Update an existing wake session (save mutated state)
    fn update_wake_session(&self, session: &crate::wake_token::WakeSession) -> Result<()>;

    /// Delete a wake session (cleanup after ritual completes)
    fn delete_wake_session(&self, session_id: &str) -> Result<()>;

    // =========================================================================
    // MIGRATION & INTROSPECTION
    // =========================================================================

    /// List tables (for migration status)
    fn list_tables(&self) -> Result<Vec<String>>;
}

/// Factory function to create the SurrealDB store
pub fn create_store(db_path: &Path) -> Result<Box<dyn KnowledgeStore>> {
    create_store_with_verbose(db_path, false)
}

/// Factory function with verbose control
pub fn create_store_with_verbose(db_path: &Path, verbose: bool) -> Result<Box<dyn KnowledgeStore>> {
    let surreal_path = db_path.with_extension("surreal");
    Ok(Box::new(
        crate::surreal_db::SurrealDatabase::open_with_verbose(surreal_path, verbose)?,
    ))
}
