use serde::{Deserialize, Serialize};
use uuid::Uuid;

use super::{ReferenceIntent, RelationshipType};

/// Namespace UUID for knot entity generation.
/// All entity UUIDs are derived from this namespace using UUID v5.
/// This ensures deterministic, reproducible UUIDs across indexing runs.
pub const NAMESPACE_KNOT: Uuid = Uuid::from_bytes([
    0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x72, 0x2d, 0x76, 0x35, 0x00,
]);

/// The kind of code entity extracted from the AST.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityKind {
    Class,
    Interface,
    Method,
    Function,
    Constant,
    Enum,
    // Kotlin-specific entities
    KotlinClass,           // class declarations
    KotlinInterface,       // interface declarations
    KotlinObject,          // object declarations (singletons)
    KotlinCompanionObject, // companion object declarations
    KotlinFunction,        // top-level and extension functions
    KotlinMethod,          // methods inside classes
    KotlinProperty,        // properties (val/var)
    KotlinEnum,            // enum class declarations
    // HTML/Web Components entities
    HtmlElement, // Custom elements like <app-profile>, <web-component>
    HtmlId,      // id="..." attributes
    HtmlClass,   // class="..." or className="..." attributes
    // CSS entities
    CssClass,    // .my-class
    CssId,       // #my-id
    CssVariable, // --my-var (CSS Custom Properties)
    // SCSS entities
    ScssVariable, // $my-var
    ScssMixin,    // @mixin my-mixin()
    ScssFunction, // @function my-function()
    // Rust entities
    RustStruct,      // struct definitions
    RustEnum,        // enum definitions
    RustUnion,       // union definitions
    RustTrait,       // trait definitions
    RustImpl,        // impl blocks (inherent and trait)
    RustFunction,    // top-level functions
    RustMethod,      // methods inside impl blocks
    RustMacroDef,    // macro_rules! definitions
    RustTypeAlias,   // type aliases
    RustConstant,    // const definitions
    RustStatic,      // static variable definitions
    RustModule,      // module declarations
    RustMacroInvoke, // macro invocations
    // Python entities
    PythonClass,    // class definitions
    PythonFunction, // top-level functions
    PythonMethod,   // methods inside classes
    PythonModule,   // module-level entities (docstrings, constants)
    PythonConstant, // module-level constants (UPPER_CASE)
    // C/C++ entities
    CStruct,         // struct definitions
    CFunction,       // top-level functions
    CppClass,        // class definitions
    CppMethod,       // methods inside classes
    CppNamespace,    // namespace definitions
    MacroDefinition, // #define macros
    // Build Systems & CI/CD entities
    BuildDependency, // Maven/Gradle dependency (groupId:artifactId:version)
    BuildPlugin,     // Maven/Gradle build plugin
    BuildTask,       // Gradle task definition
    PipelineStage,   // Jenkins pipeline stage
    PipelineStep,    // Jenkins pipeline step inside a stage
    // Groovy specific entities (beyond build scripts)
    GroovyClass,     // class declarations
    GroovyInterface, // interface declarations
    GroovyTrait,     // trait declarations
    GroovyMethod,    // methods inside classes/traits
    GroovyFunction,  // top-level methods/closures in scripts
    GroovyEnum,      // enum declarations
    GroovyProperty,  // fields/variables
    // Cargo (Rust build system)
    CargoPackage,    // [package] metadata (name, version, edition)
    CargoFeature,    // [features] flag definitions
    WorkspaceMember, // [workspace.members] entries
    // Configuration entities
    ConfigProperty, // key=value leaf in .properties, YAML, or JSON config files
    // Kubernetes entities
    K8sDeployment, // kind: Deployment
    K8sService,    // kind: Service
    K8sConfigMap,  // kind: ConfigMap
    K8sSecret,     // kind: Secret
    K8sIngress,    // kind: Ingress
    K8sNamespace,  // kind: Namespace
    K8sResource,   // Catch-all for other K8s kinds
    // Helm entities
    HelmChart,       // Chart.yaml metadata
    HelmValue,       // values.yaml key-value pairs
    HelmTemplateVar, // {{ .Values.x }} template variable usage
    // Cross-repo linking
    ProjectIdentity, // Build file project declaration (Maven GAV, Cargo package, npm name)
}

impl std::fmt::Display for EntityKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match self {
            EntityKind::Class => "class",
            EntityKind::Interface => "interface",
            EntityKind::Method => "method",
            EntityKind::Function => "function",
            EntityKind::Constant => "constant",
            EntityKind::Enum => "enum",
            EntityKind::KotlinClass => "kotlin_class",
            EntityKind::KotlinInterface => "kotlin_interface",
            EntityKind::KotlinObject => "kotlin_object",
            EntityKind::KotlinCompanionObject => "kotlin_companion_object",
            EntityKind::KotlinFunction => "kotlin_function",
            EntityKind::KotlinMethod => "kotlin_method",
            EntityKind::KotlinProperty => "kotlin_property",
            EntityKind::KotlinEnum => "kotlin_enum",
            EntityKind::HtmlElement => "html_element",
            EntityKind::HtmlId => "html_id",
            EntityKind::HtmlClass => "html_class",
            EntityKind::CssClass => "css_class",
            EntityKind::CssId => "css_id",
            EntityKind::CssVariable => "css_variable",
            EntityKind::ScssVariable => "scss_variable",
            EntityKind::ScssMixin => "scss_mixin",
            EntityKind::ScssFunction => "scss_function",
            EntityKind::RustStruct => "rust_struct",
            EntityKind::RustEnum => "rust_enum",
            EntityKind::RustUnion => "rust_union",
            EntityKind::RustTrait => "rust_trait",
            EntityKind::RustImpl => "rust_impl",
            EntityKind::RustFunction => "rust_function",
            EntityKind::RustMethod => "rust_method",
            EntityKind::RustMacroDef => "rust_macro_def",
            EntityKind::RustTypeAlias => "rust_type_alias",
            EntityKind::RustConstant => "rust_constant",
            EntityKind::RustStatic => "rust_static",
            EntityKind::RustModule => "rust_module",
            EntityKind::RustMacroInvoke => "rust_macro_invoke",
            EntityKind::PythonClass => "python_class",
            EntityKind::PythonFunction => "python_function",
            EntityKind::PythonMethod => "python_method",
            EntityKind::PythonModule => "python_module",
            EntityKind::PythonConstant => "python_constant",
            EntityKind::BuildDependency => "build_dependency",
            EntityKind::BuildPlugin => "build_plugin",
            EntityKind::BuildTask => "build_task",
            EntityKind::PipelineStage => "pipeline_stage",
            EntityKind::PipelineStep => "pipeline_step",
            EntityKind::GroovyClass => "groovy_class",
            EntityKind::GroovyInterface => "groovy_interface",
            EntityKind::GroovyTrait => "groovy_trait",
            EntityKind::GroovyMethod => "groovy_method",
            EntityKind::GroovyFunction => "groovy_function",
            EntityKind::GroovyEnum => "groovy_enum",
            EntityKind::GroovyProperty => "groovy_property",
            EntityKind::CStruct => "c_struct",
            EntityKind::CFunction => "c_function",
            EntityKind::CppClass => "cpp_class",
            EntityKind::CppMethod => "cpp_method",
            EntityKind::CppNamespace => "cpp_namespace",
            EntityKind::MacroDefinition => "macro_definition",
            EntityKind::CargoPackage => "cargo_package",
            EntityKind::CargoFeature => "cargo_feature",
            EntityKind::WorkspaceMember => "workspace_member",
            EntityKind::ConfigProperty => "config_property",
            EntityKind::K8sDeployment => "k8s_deployment",
            EntityKind::K8sService => "k8s_service",
            EntityKind::K8sConfigMap => "k8s_configmap",
            EntityKind::K8sSecret => "k8s_secret",
            EntityKind::K8sIngress => "k8s_ingress",
            EntityKind::K8sNamespace => "k8s_namespace",
            EntityKind::K8sResource => "k8s_resource",
            EntityKind::HelmChart => "helm_chart",
            EntityKind::HelmValue => "helm_value",
            EntityKind::HelmTemplateVar => "helm_template_var",
            EntityKind::ProjectIdentity => "project_identity",
        };
        write!(f, "{s}")
    }
}

/// A raw entity extracted from the AST before embedding.
///
/// Created in Stage 2 (parse) and enriched with a UUID in Stage 3 (prepare).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParsedEntity {
    /// Unique identifier — bridges Qdrant and Neo4j records.
    pub uuid: Uuid,

    /// Human-readable name (class name, method name, function name).
    pub name: String,

    /// The kind of code construct this entity represents.
    pub kind: EntityKind,

    /// Fully qualified name for disambiguation.
    /// - For Java: `com.example.connector.AlternativeConnectorService.proxyManager`
    /// - For TypeScript: `ClassName.methodName` or just `functionName` for top-level functions
    /// - For classes/interfaces: `com.example.connector.AlternativeConnectorService`
    pub fqn: String,

    /// Full signature string for methods/functions (e.g. `public void foo(int x)`).
    /// `None` for class/interface entities.
    pub signature: Option<String>,

    /// Associated documentation: JavaDoc, JSDoc, or preceding comments found
    /// immediately before this node in the source file.
    /// This captures comment blocks that directly precede declarations.
    pub docstring: Option<String>,

    /// Inline comments found within this entity's body.
    /// For classes: comments inside the class body but outside any methods.
    /// For methods/functions: comments inside the method/function body.
    /// These provide implementation context and are associated with the containing entity.
    pub inline_comments: Vec<String>,

    /// Decorators and annotations applied to this entity.
    /// Examples: `@Override`, `@OnEvent('foo')`, `@GetMapping("/path")`, etc.
    /// Populated during the parse stage for methods, functions, classes, and constants.
    pub decorators: Vec<String>,

    /// For Rust: list of attributes/decorators
    /// e.g., vec!["derive(Clone)", "tokio::main"]
    pub rust_attributes: Option<Vec<String>>,

    /// For Rust `impl` blocks: the trait being implemented (if any)
    /// e.g., Some("Debug") for `impl Debug for MyStruct`
    pub impl_trait: Option<String>,

    /// For Rust `impl` blocks: the target type being implemented for
    /// e.g., "MyStruct" for `impl MyStruct { ... }`
    pub impl_target: Option<String>,

    /// For Rust generic types: the full generic signature
    /// e.g., "<T: Clone, U: Default>" or "<'a, T>"
    pub generics: Option<String>,

    /// For Rust: lifetime parameters
    /// e.g., vec!["'a", "'b"]
    pub lifetimes: Option<Vec<String>>,

    /// For JS/TS import aliases: if this entity is a `require()` or `import` alias,
    /// the relative module path (e.g., "./ResolveCache", "../util/fs").
    /// Used during resolution to follow cross-file aliases to the original definition.
    pub alias_module_path: Option<String>,

    /// For TS named imports: the original export name in the target module.
    /// E.g., `import { MyTsTarget as MyTsAlias }` → Some("MyTsTarget").
    /// Used by the alias resolver to look up the correct entity in the target file.
    pub original_export_name: Option<String>,

    /// For JS/TS module-level entities (`<module>`): the name exported as default
    /// (e.g., `module.exports = ResolveCache`). Used by the alias resolver
    /// to resolve bare module imports to the correct entity within the target file.
    pub default_export: Option<String>,

    /// Source language (`"java"` or `"typescript"`).
    pub language: String,

    /// Absolute path of the source file containing this entity.
    pub file_path: String,

    /// 1-based line number where the entity declaration starts.
    pub start_line: usize,

    /// 1-based line number where the entity declaration ends.
    /// Used to determine if an orphaned reference is contained within this entity.
    pub end_line: usize,

    /// Enclosing class name for methods. Used to resolve local calls.
    /// `None` for class/interface entities and top-level functions.
    pub enclosing_class: Option<String>,

    /// Fully qualified name of the enclosing class for CONTAINS disambiguation.
    /// When set, the Neo4j CONTAINS auto-link uses this FQN to match the exact
    /// parent entity instead of matching by bare `name`, which can collide
    /// when multiple entities share the same class name (e.g. fixtures vs real).
    /// Populated by language-specific qualifier passes; `None` for non-Rust or
    /// when the enclosing class FQN is unknown.
    pub enclosing_class_fqn: Option<String>,

    /// Logical repository name for multi-repository isolation.
    /// Used to separate entities from different codebases in shared databases.
    /// Example: "my-java-repo", "my-typescript-repo", "my-microservice"
    pub repo_name: String,

    /// `true` when this entity lives under a Rust `#[cfg(test)]` module
    /// (or any nesting depth of such modules). Surfaced as a Neo4j property
    /// so MCP consumers can distinguish callers/callees that come from
    /// test code (living documentation) versus production code.
    /// Always `false` for non-Rust languages.
    pub is_test_context: bool,

    /// Raw reference intents extracted from this entity.
    /// Populated during the parse stage; may be empty if no references were found.
    /// These are resolved to UUIDs during the ingest stage and converted to graph relationships.
    pub reference_intents: Vec<ReferenceIntent>,

    /// UUIDs of entities this entity directly calls / depends on.
    /// **DEPRECATED:** Use `relationships` instead for typed edges.
    /// Populated during the ingest stage based on reference_intents resolution.
    /// Kept for backward compatibility.
    pub calls: Vec<Uuid>,

    /// Typed relationships to other entities.
    /// Each edge includes the target UUID and the relationship type (CALLS, EXTENDS, IMPLEMENTS, REFERENCES).
    /// Populated during the ingest stage based on reference_intents resolution.
    pub relationships: Vec<(Uuid, RelationshipType)>,

    /// The text that will be fed to the embedding model.
    /// Constructed in Stage 3 (prepare) from `name`, `signature`, and `docstring`.
    pub embed_text: String,
}

/// Minimal representation of an entity for relationship resolution.
///
/// Retains only the fields necessary to match reference intents to UUIDs,
/// allowing the heavy `embed_text`, `docstring`, and `vector` to be freed.
#[derive(Debug, Clone)]
pub struct ResolutionEntity {
    pub uuid: Uuid,
    pub name: String,
    pub fqn: String,
    pub file_path: String,
    pub kind: EntityKind,
    pub enclosing_class: Option<String>,
    pub enclosing_class_fqn: Option<String>,
    pub signature: Option<String>,
    pub reference_intents: Vec<ReferenceIntent>,
    pub relationships: Vec<(Uuid, RelationshipType)>,
    pub alias_module_path: Option<String>,
    pub original_export_name: Option<String>,
    pub default_export: Option<String>,
    pub is_test_context: bool,
}

impl From<&ParsedEntity> for ResolutionEntity {
    fn from(entity: &ParsedEntity) -> Self {
        Self {
            uuid: entity.uuid,
            name: entity.name.clone(),
            fqn: entity.fqn.clone(),
            file_path: entity.file_path.clone(),
            kind: entity.kind.clone(),
            enclosing_class: entity.enclosing_class.clone(),
            enclosing_class_fqn: entity.enclosing_class_fqn.clone(),
            signature: entity.signature.clone(),
            reference_intents: entity.reference_intents.clone(),
            relationships: Vec::new(),
            alias_module_path: entity.alias_module_path.clone(),
            original_export_name: entity.original_export_name.clone(),
            default_export: entity.default_export.clone(),
            is_test_context: entity.is_test_context,
        }
    }
}

impl From<&EmbeddedEntity> for ResolutionEntity {
    fn from(ee: &EmbeddedEntity) -> Self {
        let mut e = ResolutionEntity::from(&ee.entity);
        e.signature = ee.entity.signature.clone();
        e
    }
}

impl ParsedEntity {
    /// Create a new entity with a deterministic UUID v5.
    ///
    /// The UUID is derived from the entity's unique identity (repo_name:file_path:fqn)
    /// to ensure the same entity always receives the same UUID across indexing runs.
    /// This is critical for incremental indexing to avoid breaking graph relationships.
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        name: impl Into<String>,
        kind: EntityKind,
        fqn: impl Into<String>,
        signature: Option<String>,
        docstring: Option<String>,
        language: impl Into<String>,
        file_path: impl Into<String>,
        start_line: usize,
        end_line: usize,
        enclosing_class: Option<String>,
        repo_name: impl Into<String>,
    ) -> Self {
        let name = name.into();
        let fqn = fqn.into();
        let file_path = file_path.into();
        let repo_name = repo_name.into();

        // Generate deterministic UUID from unique identity
        let identity = format!("{}:{}:{}:{}", repo_name, file_path, fqn, start_line);
        let uuid = Uuid::new_v5(&NAMESPACE_KNOT, identity.as_bytes());

        Self {
            uuid,
            name,
            kind,
            fqn,
            signature,
            docstring,
            language: language.into(),
            file_path,
            start_line,
            end_line,
            enclosing_class,
            repo_name,
            reference_intents: Vec::new(),
            calls: Vec::new(),
            relationships: Vec::new(),
            inline_comments: Vec::new(),
            decorators: Vec::new(),
            rust_attributes: None,
            impl_trait: None,
            impl_target: None,
            generics: None,
            lifetimes: None,
            alias_module_path: None,
            original_export_name: None,
            default_export: None,
            enclosing_class_fqn: None,
            is_test_context: false,
            embed_text: String::new(),
        }
    }
}

/// An entity that has been embedded and is ready for dual-write ingestion.
///
/// Produced by Stage 4 (embed) and consumed by Stage 5 (ingest).
#[derive(Debug, Clone)]
pub struct EmbeddedEntity {
    /// The original parsed entity with all metadata.
    pub entity: ParsedEntity,

    /// High-dimensional vector produced by the embedding model.
    pub vector: Vec<f32>,
}
