use async_lsp::lsp_types::Range;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Represents a complete Avro schema document
#[derive(Debug, Clone, PartialEq)]
pub struct AvroSchema {
    pub root: AvroType,
    pub named_types: HashMap<String, AvroType>,
    /// Errors collected during parsing (for error recovery)
    pub parse_errors: Vec<super::error::SchemaError>,
    /// Semantic tokens captured during parsing
    pub semantic_tokens: Vec<SemanticTokenData>,
    /// Warnings collected during validation (non-blocking issues)
    pub warnings: Vec<super::warning::SchemaWarning>,
}

/// Represents an Avro type
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AvroType {
    Primitive(PrimitiveType),
    PrimitiveObject(PrimitiveSchema),
    Record(RecordSchema),
    Enum(EnumSchema),
    Array(ArraySchema),
    Map(MapSchema),
    Union(UnionSchema),
    Fixed(FixedSchema),
    /// Reference to a named type by string
    TypeRef(TypeRefSchema),
    /// Invalid type encountered during parsing (used for error recovery)
    #[serde(skip)]
    Invalid(InvalidTypeSchema),
}

/// A reference to a named type
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TypeRefSchema {
    pub name: String,
    #[serde(skip)]
    pub range: Option<Range>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum PrimitiveType {
    #[default]
    Null,
    Boolean,
    Int,
    Long,
    Float,
    Double,
    Bytes,
    String,
}

impl PrimitiveType {
    pub fn parse(s: &str) -> Option<Self> {
        match s {
            "null" => Some(PrimitiveType::Null),
            "boolean" => Some(PrimitiveType::Boolean),
            "int" => Some(PrimitiveType::Int),
            "long" => Some(PrimitiveType::Long),
            "float" => Some(PrimitiveType::Float),
            "double" => Some(PrimitiveType::Double),
            "bytes" => Some(PrimitiveType::Bytes),
            "string" => Some(PrimitiveType::String),
            _ => None,
        }
    }
}

/// Represents a primitive type with optional logical type metadata
/// Used for cases like {"type": "int", "logicalType": "date"}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PrimitiveSchema {
    #[serde(rename = "type")]
    pub type_name: String,
    #[serde(skip)]
    pub primitive_type: PrimitiveType,
    #[serde(skip_serializing_if = "Option::is_none", rename = "logicalType")]
    pub logical_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub precision: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scale: Option<usize>,

    // Position tracking (not serialized)
    #[serde(skip)]
    pub range: Option<Range>,
    #[serde(skip)]
    pub name_range: Option<Range>,
    #[serde(skip)]
    pub namespace_range: Option<Range>,
    #[serde(skip)]
    pub type_name_range: Option<Range>,
    #[serde(skip)]
    pub logical_type_range: Option<Range>,
}

/// Semantic token data captured during parsing
#[derive(Debug, Clone, PartialEq)]
pub struct SemanticTokenData {
    pub range: Range,
    pub token_type: SemanticTokenType,
    pub modifiers: SemanticTokenModifiers,
}

/// Types of semantic tokens
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SemanticTokenType {
    Keyword,    // JSON keys: "type", "name", etc.
    Type,       // Type names: "string", "int", "Address"
    Enum,       // Enum type names
    Struct,     // Record type names
    Property,   // Field names
    EnumMember, // Enum symbol values
}

bitflags::bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub struct SemanticTokenModifiers: u32 {
        const NONE = 0x00;
        const DECLARATION = 0x01;
        const READONLY = 0x02;
    }
}

/// Represents an invalid type that failed validation during parsing
/// Used for error recovery to allow partial AST building
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InvalidTypeSchema {
    /// The invalid type name that was encountered
    pub type_name: String,
    /// Position information for the invalid type
    #[serde(skip)]
    pub range: Option<Range>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RecordSchema {
    #[serde(rename = "type")]
    pub type_name: String,
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub namespace: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub doc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aliases: Option<Vec<String>>,
    pub fields: Vec<Field>,

    // Position tracking (not serialized)
    #[serde(skip)]
    pub range: Option<Range>,
    #[serde(skip)]
    pub name_range: Option<Range>,
    #[serde(skip)]
    pub namespace_range: Option<Range>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Field {
    pub name: String,
    #[serde(rename = "type")]
    pub field_type: Box<AvroType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub doc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aliases: Option<Vec<String>>,

    // Position tracking (not serialized)
    #[serde(skip)]
    pub range: Option<Range>,
    #[serde(skip)]
    pub name_range: Option<Range>,
    #[serde(skip)]
    pub namespace_range: Option<Range>,
    #[serde(skip)]
    pub type_name_range: Option<Range>,
    #[serde(skip)]
    pub logical_type_range: Option<Range>,
    #[serde(skip)]
    pub type_range: Option<Range>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EnumSchema {
    #[serde(rename = "type")]
    pub type_name: String,
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub namespace: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub doc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aliases: Option<Vec<String>>,
    pub symbols: Vec<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default: Option<String>,

    // Position tracking (not serialized)
    #[serde(skip)]
    pub range: Option<Range>,
    #[serde(skip)]
    pub name_range: Option<Range>,
    #[serde(skip)]
    pub namespace_range: Option<Range>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArraySchema {
    #[serde(rename = "type")]
    pub type_name: String,
    pub items: Box<AvroType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default: Option<Vec<serde_json::Value>>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MapSchema {
    #[serde(rename = "type")]
    pub type_name: String,
    pub values: Box<AvroType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default: Option<HashMap<String, serde_json::Value>>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct UnionSchema {
    pub types: Vec<AvroType>,
    pub range: Option<Range>,
}

// Manual Serialize/Deserialize to treat as transparent (serialize/deserialize only the types vec)
impl serde::Serialize for UnionSchema {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.types.serialize(serializer)
    }
}

impl<'de> serde::Deserialize<'de> for UnionSchema {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Ok(UnionSchema {
            types: Vec::<AvroType>::deserialize(deserializer)?,
            range: None,
        })
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FixedSchema {
    #[serde(rename = "type")]
    pub type_name: String,
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub namespace: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub doc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aliases: Option<Vec<String>>,
    pub size: usize,
    #[serde(skip_serializing_if = "Option::is_none", rename = "logicalType")]
    pub logical_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub precision: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scale: Option<usize>,

    // Position tracking (not serialized)
    #[serde(skip)]
    pub range: Option<Range>,
    #[serde(skip)]
    pub name_range: Option<Range>,
    #[serde(skip)]
    pub namespace_range: Option<Range>,
}
