use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::time::Duration;

// ===== SCROLL (Specification) =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Scroll {
    pub id: String,
    pub title: String,
    pub intent: String,
    pub constraints: Vec<Constraint>,
    pub tasks: Vec<Task>,
    pub metadata: ScrollMetadata,
    pub status: ScrollStatus,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    /// Soft TTL: when this scroll is scheduled for cleanup (set on Completed/Failed).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cleanup_after: Option<DateTime<Utc>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScrollMetadata {
    pub execution_mode: ExecutionMode,
    pub auto_forge_config: Option<AutoForgeConfig>,
    pub recipe_used: Option<String>,
    pub search_context: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ScrollStatus {
    Draft,
    Validated,
    InProgress,
    Completed,
    Failed,
    Archived,
}

impl ScrollStatus {
    /// Validate that a transition from `self` to `next` is allowed.
    pub fn can_transition_to(&self, next: &ScrollStatus) -> bool {
        matches!(
            (self, next),
            // Draft → Validated | Archived
            (ScrollStatus::Draft, ScrollStatus::Validated | ScrollStatus::Archived)
            // Validated → InProgress | Archived
            | (ScrollStatus::Validated, ScrollStatus::InProgress | ScrollStatus::Archived)
            // InProgress → Completed | Failed | Archived
            | (ScrollStatus::InProgress, ScrollStatus::Completed | ScrollStatus::Failed | ScrollStatus::Archived)
        )
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ExecutionMode {
    Forge,
    AutoForge,
}

// ===== SCROLL EVENT LISTENER =====

/// Trait for components that react to scroll status changes.
///
/// Implement this to receive notifications when a scroll's status is updated
/// (e.g., Draft → InProgress → Completed). Used by the sync bridge to
/// auto-push status changes to linked external systems.
#[async_trait::async_trait]
pub trait ScrollEventListener: Send + Sync {
    async fn on_scroll_status_changed(
        &self,
        scroll_id: &str,
        new_status: &ScrollStatus,
    );
}

// ===== CLEANUP =====

#[derive(Debug, Clone, Default)]
pub struct CleanupStats {
    pub scrolls_deleted: usize,
    pub tasks_deleted: usize,
    pub validation_reports_deleted: usize,
    pub auto_forge_logs_deleted: usize,
    pub lifecycle_events_deleted: usize,
    pub external_refs_deleted: usize,
    pub search_queries_deleted: usize,
}

// ===== LEARNED STRATEGY =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearnedStrategy {
    pub query_pattern: String,
    pub best_strategy: String,
    pub success_rate: f64,
    pub avg_latency_ms: i64,
    pub avg_cost_cents: i64,
    pub sample_count: i64,
    pub last_updated: i64,
}

// ===== CONSTRAINTS =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Constraint {
    pub id: String,
    pub constraint_type: ConstraintType,
    pub description: String,
    pub pattern: String,
    pub source: ConstraintSource,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ConstraintType {
    Must,
    MustNot,
    Should,
    ShouldNot,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ConstraintSource {
    UserSpecified,
    Discovered,
    Inferred,
}

// ===== TASKS =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Task {
    pub id: String,
    pub scroll_id: String,
    pub description: String,
    pub target_file: String,
    pub task_type: TaskType,
    pub status: TaskStatus,
    pub dependencies: Vec<String>,
    pub generated_code: Option<String>,
    pub diff: Option<String>,
    pub created_at: DateTime<Utc>,
    /// Display label for task grouping, e.g., "T1", "T1.1", "T2".
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub group_label: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum TaskType {
    Create,
    Modify,
    Delete,
    Test,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum TaskStatus {
    Pending,
    InProgress,
    Completed,
    Failed,
    Skipped,
}

// ===== SEARCH =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchRequest {
    pub query: String,
    pub max_results: usize,
    pub file_types: Vec<String>,
    pub exclude_paths: Vec<String>,
}

impl Default for SearchRequest {
    fn default() -> Self {
        Self {
            query: String::new(),
            max_results: 10,
            file_types: Vec::new(),
            exclude_paths: vec!["node_modules".into(), "target".into(), ".git".into()],
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResponse {
    pub query_id: String,
    pub results: Vec<SearchResult>,
    pub source: SearchSource,
    #[serde(skip)]
    pub latency: Duration,
    pub cost: f64,
    pub tier_breakdown: TierBreakdown,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
    pub file_path: String,
    pub content: String,
    pub relevance_score: f64,
    pub line_number: Option<usize>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SearchSource {
    Cache,
    Tier1Vector,
    Tier2Hybrid,
    Tier3DeepAgentic,
    GraphQuery,
    KnowledgeBase,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TierBreakdown {
    pub tier1_tried: bool,
    pub tier1_success: bool,
    pub tier2_tried: bool,
    pub tier2_success: bool,
    pub tier3_tried: bool,
    pub tier3_success: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum QueryType {
    ExactMatch,
    Semantic,
    Pattern,
    Architecture,
    Dependency,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SearchStrategy {
    Tier1Only,
    Tier1ThenTier2,
    Tier1ThenTier2ThenTier3,
    Tier2Only,
    Tier3Only,
    GraphQuery,
}

// ===== VALIDATION =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationReport {
    pub scroll_id: String,
    pub is_valid: bool,
    pub constraint_violations: Vec<ValidationResult>,
    pub circular_dependencies: Vec<ValidationResult>,
    pub type_errors: Vec<ValidationResult>,
    pub breaking_changes: Vec<ValidationResult>,
    pub validated_at: DateTime<Utc>,
}

impl ValidationReport {
    pub fn summarize(&self) -> String {
        let errors: Vec<&str> = self.constraint_violations.iter()
            .chain(self.circular_dependencies.iter())
            .chain(self.type_errors.iter())
            .chain(self.breaking_changes.iter())
            .filter(|r| !r.passed)
            .map(|r| r.message.as_str())
            .collect();

        if errors.is_empty() {
            "All validations passed".to_string()
        } else {
            format!("{} errors: {}", errors.len(), errors.join("; "))
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationResult {
    pub constraint_id: String,
    pub passed: bool,
    pub message: String,
    pub severity: Severity,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum Severity {
    Info,
    Warning,
    Error,
}

// ===== AUTO-FORGE =====

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AutoForgeConfig {
    pub auto_apply: bool,
    pub auto_test: bool,
    pub auto_verify: bool,
    pub max_iterations: usize,
    pub test_command: Option<String>,
}

impl Default for AutoForgeConfig {
    fn default() -> Self {
        Self {
            auto_apply: true,
            auto_test: true,
            auto_verify: true,
            max_iterations: 3,
            test_command: None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutoForgeReport {
    pub id: String,
    pub scroll_id: String,
    pub temp_branch: String,
    pub test_results: TestResults,
    pub analysis: CodeAnalysis,
    pub status: AutoForgeStatus,
    pub created_at: i64,
    /// Test specs for files where Trinity could not generate tests
    /// (e.g., MCP sampling unavailable). The host client can use these
    /// to generate tests directly.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub test_specs: Vec<serde_json::Value>,
    /// Per-file code review results from Legolas (post-apply verification).
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub code_review: Vec<CodeAnalysis>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum AutoForgeStatus {
    AllPassed,
    TestsFailed,
    AnalysisWarnings,
    Failed,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestResults {
    pub total: usize,
    pub passed: usize,
    pub failed: usize,
    pub skipped: usize,
    pub output: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodeAnalysis {
    pub issues: Vec<AnalysisIssue>,
    pub metrics: AnalysisMetrics,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisIssue {
    pub file_path: String,
    pub line: usize,
    pub severity: Severity,
    pub message: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisMetrics {
    pub complexity: f64,
    pub test_coverage: f64,
    pub dependency_count: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum UserDecision {
    Accept,
    Review,
    Reject,
    Iterate,
}

/// Decision for batch approval workflow.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum BatchDecision {
    /// Accept all changes from all tasks.
    AcceptAll,
    /// Review individual files for selective approval.
    ReviewFiles,
    /// Reject all changes and discard.
    RejectAll,
}

/// Per-task summary for batch approval report.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskApprovalSummary {
    pub task_id: String,
    pub target_file: String,
    pub task_type: TaskType,
    pub status: TaskStatus,
    pub lines_changed: usize,
    pub has_tests: bool,
}

/// Consolidated report for parallel task batch approval.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchApprovalReport {
    pub scroll_id: String,
    pub task_summaries: Vec<TaskApprovalSummary>,
    pub test_results: TestResults,
    pub analysis: CodeAnalysis,
    pub overall_status: AutoForgeStatus,
    pub files_changed: Vec<String>,
    pub total_lines_added: usize,
    pub total_lines_removed: usize,
}

// ===== KNOWLEDGE GRAPH =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileEntity {
    pub path: String,
    pub language: String,
    pub functions: Vec<FunctionDef>,
    pub types: Vec<TypeDef>,
    pub imports: Vec<ImportDef>,
    pub exports: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionDef {
    pub name: String,
    pub signature: String,
    pub line_start: usize,
    pub line_end: usize,
    pub is_public: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeDef {
    pub name: String,
    pub kind: String,
    pub fields: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportDef {
    pub source: String,
    pub items: Vec<String>,
}

// ===== RECIPES =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Recipe {
    pub name: String,
    pub description: String,
    pub template: String,
    pub variables: Vec<RecipeVariable>,
    pub llm_variant: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecipeVariable {
    pub name: String,
    pub description: String,
    pub required: bool,
    pub default: Option<String>,
}

// ===== LLM =====

#[async_trait::async_trait]
pub trait LlmProvider: Send + Sync {
    /// A short identifier for the current LLM provider (e.g. "claude", "gpt4").
    /// Used by RecipeManager to auto-select LLM-specific recipe variants.
    fn name(&self) -> &str {
        "default"
    }

    /// Whether the provider can currently generate completions.
    ///
    /// Returns `true` by default, which preserves behaviour for test mocks.
    /// `SamplingLlmProvider` overrides this to cache the result of the first
    /// sampling attempt; `PassthroughLlmProvider` always returns `false`.
    fn is_available(&self) -> bool {
        true
    }

    async fn complete(&self, prompt: &str) -> anyhow::Result<String>;
    async fn complete_with_system(&self, system: &str, prompt: &str) -> anyhow::Result<String>;
}

// ===== AGENT ACTIVITY =====

/// Records what a specific agent did during scroll execution.
/// Used to track per-agent contributions visible in IDE extensions.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentActivity {
    pub id: String,
    pub scroll_id: String,
    pub task_id: String,
    pub agent_name: String,
    pub action: String,
    pub detail: String,
    pub started_at: DateTime<Utc>,
    pub completed_at: Option<DateTime<Utc>>,
    pub status: AgentActivityStatus,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum AgentActivityStatus {
    Running,
    Completed,
    Failed,
}

// ===== PATTERNS =====

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PatternExample {
    pub file_path: String,
    pub content: String,
    pub relevance: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodeExample {
    pub file_path: String,
    pub code: String,
    pub relevance: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestExample {
    pub file_path: String,
    pub test_code: String,
    pub relevance: f64,
}
