//! Core types used throughout the aurora-semantic crate.

use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use uuid::Uuid;

/// Unique identifier for a workspace.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct WorkspaceId(pub Uuid);

impl WorkspaceId {
    /// Create a new random workspace ID.
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }

    /// Create from an existing UUID.
    pub fn from_uuid(uuid: Uuid) -> Self {
        Self(uuid)
    }

    /// Get the inner UUID.
    pub fn as_uuid(&self) -> &Uuid {
        &self.0
    }
}

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

impl std::fmt::Display for WorkspaceId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Unique identifier for a document (source file).
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct DocumentId(pub Uuid);

impl DocumentId {
    /// Create a new unique document ID.
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }
}

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

/// Unique identifier for a chunk within a document.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ChunkId(pub Uuid);

impl ChunkId {
    /// Create a new unique chunk ID.
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }
}

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

/// Represents a source code document.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Document {
    /// Unique identifier for this document.
    pub id: DocumentId,
    /// Path relative to workspace root.
    pub relative_path: PathBuf,
    /// Absolute path on disk.
    pub absolute_path: PathBuf,
    /// Programming language.
    pub language: Language,
    /// Content hash for change detection.
    pub content_hash: String,
    /// File size in bytes.
    pub size_bytes: u64,
    /// Last modification timestamp.
    pub modified_at: chrono::DateTime<chrono::Utc>,
}

/// A chunk of source code extracted from a document.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Chunk {
    /// Unique identifier for this chunk.
    pub id: ChunkId,
    /// Document this chunk belongs to.
    pub document_id: DocumentId,
    /// The actual source code content.
    pub content: String,
    /// Type of code construct this chunk represents.
    pub chunk_type: ChunkType,
    /// Starting line number (1-indexed).
    pub start_line: u32,
    /// Ending line number (1-indexed).
    pub end_line: u32,
    /// Starting byte offset.
    pub start_byte: usize,
    /// Ending byte offset.
    pub end_byte: usize,
    /// Name of the symbol (function name, class name, etc.).
    pub symbol_name: Option<String>,
    /// Parent symbol name for nested constructs.
    pub parent_symbol: Option<String>,
}

/// Type of code chunk.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ChunkType {
    /// Complete function or method.
    Function,
    /// Class or struct definition.
    Class,
    /// Struct definition (for languages that distinguish).
    Struct,
    /// Enum definition.
    Enum,
    /// Interface or trait definition.
    Interface,
    /// Module or namespace.
    Module,
    /// Import/use statements block.
    Imports,
    /// Constant or static variable.
    Constant,
    /// Type alias or typedef.
    TypeDef,
    /// Implementation block (Rust impl).
    Implementation,
    /// Generic code block (fallback).
    Block,
    /// Comment block or documentation.
    Comment,
}

/// Supported programming languages.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Language {
    /// Rust programming language.
    Rust,
    /// Python programming language.
    Python,
    /// JavaScript (ES6+).
    JavaScript,
    /// TypeScript (including TSX/JSX).
    TypeScript,
    /// Go programming language.
    Go,
    /// C programming language.
    C,
    /// C++ programming language.
    Cpp,
    /// Java programming language.
    Java,
    /// Unknown or unsupported language.
    Unknown,
}

impl Language {
    /// Detect language from file extension.
    pub fn from_extension(ext: &str) -> Self {
        match ext.to_lowercase().as_str() {
            "rs" => Language::Rust,
            "py" | "pyw" | "pyi" => Language::Python,
            "js" | "mjs" | "cjs" => Language::JavaScript,
            "ts" | "mts" | "cts" => Language::TypeScript,
            "tsx" | "jsx" => Language::TypeScript, // TSX/JSX treated as TypeScript
            "go" => Language::Go,
            "c" | "h" => Language::C,
            "cpp" | "cc" | "cxx" | "hpp" | "hxx" | "hh" => Language::Cpp,
            "java" => Language::Java,
            _ => Language::Unknown,
        }
    }

    /// Get file extensions for this language.
    pub fn extensions(&self) -> &'static [&'static str] {
        match self {
            Language::Rust => &["rs"],
            Language::Python => &["py", "pyw", "pyi"],
            Language::JavaScript => &["js", "mjs", "cjs", "jsx"],
            Language::TypeScript => &["ts", "mts", "cts", "tsx"],
            Language::Go => &["go"],
            Language::C => &["c", "h"],
            Language::Cpp => &["cpp", "cc", "cxx", "hpp", "hxx", "hh"],
            Language::Java => &["java"],
            Language::Unknown => &[],
        }
    }

    /// Get the language name as a string.
    pub fn as_str(&self) -> &'static str {
        match self {
            Language::Rust => "rust",
            Language::Python => "python",
            Language::JavaScript => "javascript",
            Language::TypeScript => "typescript",
            Language::Go => "go",
            Language::C => "c",
            Language::Cpp => "cpp",
            Language::Java => "java",
            Language::Unknown => "unknown",
        }
    }
}

/// A search result with relevance score.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
    /// The matching chunk.
    pub chunk: Chunk,
    /// Document information.
    pub document: Document,
    /// Relevance score (0.0 to 1.0).
    pub score: f32,
    /// Which search method found this result.
    pub match_type: MatchType,
    /// Highlighted snippets showing matches.
    pub highlights: Vec<Highlight>,
}

/// Type of match that produced a search result.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MatchType {
    /// Found via lexical (keyword) search.
    Lexical,
    /// Found via semantic (embedding) search.
    Semantic,
    /// Found via hybrid search (combined score).
    Hybrid,
}

/// A highlighted portion of text showing a match.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Highlight {
    /// Start offset within the chunk content.
    pub start: usize,
    /// End offset within the chunk content.
    pub end: usize,
    /// The matched text.
    pub text: String,
}

/// Progress information during indexing.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexProgress {
    /// Current phase of indexing.
    pub phase: IndexPhase,
    /// Number of items processed.
    pub processed: usize,
    /// Total number of items to process.
    pub total: usize,
    /// Current file being processed (if applicable).
    pub current_file: Option<PathBuf>,
    /// Estimated time remaining in seconds.
    pub eta_seconds: Option<f64>,
}

impl IndexProgress {
    /// Get progress as a percentage (0.0 to 100.0).
    pub fn percentage(&self) -> f64 {
        if self.total == 0 {
            0.0
        } else {
            (self.processed as f64 / self.total as f64) * 100.0
        }
    }
}

/// Phases of the indexing process.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum IndexPhase {
    /// Scanning filesystem for files.
    Scanning,
    /// Parsing source code into chunks.
    Parsing,
    /// Generating embeddings for semantic search.
    Embedding,
    /// Building search indexes.
    Indexing,
    /// Writing index to disk.
    Persisting,
    /// Indexing complete.
    Complete,
}

/// Statistics about an indexed workspace.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceStats {
    /// Workspace identifier.
    pub workspace_id: WorkspaceId,
    /// Root path of the workspace.
    pub root_path: PathBuf,
    /// Total number of indexed documents.
    pub document_count: usize,
    /// Total number of chunks.
    pub chunk_count: usize,
    /// Total size of source files in bytes.
    pub total_bytes: u64,
    /// Size of the index on disk in bytes.
    pub index_size_bytes: u64,
    /// When the index was created.
    pub created_at: chrono::DateTime<chrono::Utc>,
    /// When the index was last updated.
    pub updated_at: chrono::DateTime<chrono::Utc>,
    /// Breakdown by language.
    pub language_stats: Vec<LanguageStats>,
}

/// Statistics for a single language in a workspace.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LanguageStats {
    /// The language.
    pub language: Language,
    /// Number of files.
    pub file_count: usize,
    /// Number of chunks.
    pub chunk_count: usize,
    /// Total bytes.
    pub total_bytes: u64,
}
