use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;
use std::sync::Arc;

// Schema mirrors the Go Punc Generator's schema struct for consistency.
// It is an order-preserving representation of a JSON Schema.

pub fn deserialize_some<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error>
where
  D: serde::Deserializer<'de>,
{
  let v = Value::deserialize(deserializer)?;
  Ok(Some(v))
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SchemaObject {
  // Core Schema Keywords
  #[serde(rename = "$id")]
  pub id: Option<String>,
  #[serde(rename = "$ref")]
  pub r#ref: Option<String>,
  /*
     Note: The `Ref` field in the Go struct is a pointer populated by the linker.
     In Rust, we might handle this differently (e.g., separate lookup or Rc/Arc),
     so we omit the direct recursive `Ref` field for now and rely on `ref_string`.
  */
  pub description: Option<String>,
  pub title: Option<String>,
  #[serde(default)] // Allow missing type
  #[serde(rename = "type")]
  pub type_: Option<SchemaTypeOrArray>, // Handles string or array of strings

  // Object Keywords
  pub properties: Option<BTreeMap<String, Arc<Schema>>>,
  #[serde(rename = "patternProperties")]
  pub pattern_properties: Option<BTreeMap<String, Arc<Schema>>>,
  #[serde(rename = "additionalProperties")]
  pub additional_properties: Option<Arc<Schema>>,
  #[serde(rename = "$family")]
  pub family: Option<String>,

  pub required: Option<Vec<String>>,

  // dependencies can be schema dependencies or property dependencies
  pub dependencies: Option<BTreeMap<String, Dependency>>,

  // Array Keywords
  #[serde(rename = "items")]
  pub items: Option<Arc<Schema>>,
  #[serde(rename = "prefixItems")]
  pub prefix_items: Option<Vec<Arc<Schema>>>,

  // String Validation
  #[serde(rename = "minLength")]
  pub min_length: Option<f64>,
  #[serde(rename = "maxLength")]
  pub max_length: Option<f64>,
  pub pattern: Option<String>,

  // Array Validation
  #[serde(rename = "minItems")]
  pub min_items: Option<f64>,
  #[serde(rename = "maxItems")]
  pub max_items: Option<f64>,
  #[serde(rename = "uniqueItems")]
  pub unique_items: Option<bool>,
  #[serde(rename = "contains")]
  pub contains: Option<Arc<Schema>>,
  #[serde(rename = "minContains")]
  pub min_contains: Option<f64>,
  #[serde(rename = "maxContains")]
  pub max_contains: Option<f64>,

  // Object Validation
  #[serde(rename = "minProperties")]
  pub min_properties: Option<f64>,
  #[serde(rename = "maxProperties")]
  pub max_properties: Option<f64>,
  #[serde(rename = "propertyNames")]
  pub property_names: Option<Arc<Schema>>,

  // Numeric Validation
  pub format: Option<String>,
  #[serde(rename = "enum")]
  pub enum_: Option<Vec<Value>>, // `enum` is a reserved keyword in Rust
  #[serde(
    default,
    rename = "const",
    deserialize_with = "crate::database::schema::deserialize_some"
  )]
  pub const_: Option<Value>,

  // Numeric Validation
  #[serde(rename = "multipleOf")]
  pub multiple_of: Option<f64>,
  pub minimum: Option<f64>,
  pub maximum: Option<f64>,
  #[serde(rename = "exclusiveMinimum")]
  pub exclusive_minimum: Option<f64>,
  #[serde(rename = "exclusiveMaximum")]
  pub exclusive_maximum: Option<f64>,

  // Combining Keywords
  #[serde(rename = "allOf")]
  pub all_of: Option<Vec<Arc<Schema>>>,
  #[serde(rename = "oneOf")]
  pub one_of: Option<Vec<Arc<Schema>>>,
  #[serde(rename = "not")]
  pub not: Option<Arc<Schema>>,
  #[serde(rename = "if")]
  pub if_: Option<Arc<Schema>>,
  #[serde(rename = "then")]
  pub then_: Option<Arc<Schema>>,
  #[serde(rename = "else")]
  pub else_: Option<Arc<Schema>>,

  // Custom Vocabularies
  pub form: Option<Vec<String>>,
  pub display: Option<Vec<String>>,
  #[serde(rename = "enumNames")]
  pub enum_names: Option<Vec<String>>,
  pub control: Option<String>,
  pub actions: Option<BTreeMap<String, Action>>,
  pub computer: Option<String>,
  #[serde(default)]
  pub extensible: Option<bool>,

  #[serde(skip)]
  pub compiled_format: Option<CompiledFormat>,
  #[serde(skip)]
  pub compiled_pattern: Option<CompiledRegex>,
  #[serde(skip)]
  pub compiled_pattern_properties: Option<Vec<(CompiledRegex, Arc<Schema>)>>,
}

/// Represents a compiled format validator
#[derive(Clone)]
pub enum CompiledFormat {
  Func(fn(&serde_json::Value) -> Result<(), Box<dyn std::error::Error + Send + Sync>>),
  Regex(regex::Regex),
}

impl std::fmt::Debug for CompiledFormat {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      CompiledFormat::Func(_) => write!(f, "CompiledFormat::Func(...)"),
      CompiledFormat::Regex(r) => write!(f, "CompiledFormat::Regex({:?})", r),
    }
  }
}

/// A wrapper for compiled regex patterns
#[derive(Debug, Clone)]
pub struct CompiledRegex(pub regex::Regex);

#[derive(Debug, Clone, Serialize, Default)]
pub struct Schema {
  #[serde(flatten)]
  pub obj: SchemaObject,
  #[serde(skip)]
  pub always_fail: bool,
}

impl std::ops::Deref for Schema {
  type Target = SchemaObject;
  fn deref(&self) -> &Self::Target {
    &self.obj
  }
}
impl std::ops::DerefMut for Schema {
  fn deref_mut(&mut self) -> &mut Self::Target {
    &mut self.obj
  }
}

impl Schema {
  pub fn compile_internals(&mut self) {
    self.map_children(|child| child.compile_internals());

    if let Some(format_str) = &self.obj.format
      && let Some(fmt) = crate::database::formats::FORMATS.get(format_str.as_str())
    {
      self.obj.compiled_format = Some(crate::database::schema::CompiledFormat::Func(fmt.func));
    }

    if let Some(pattern_str) = &self.obj.pattern
      && let Ok(re) = regex::Regex::new(pattern_str)
    {
      self.obj.compiled_pattern = Some(crate::database::schema::CompiledRegex(re));
    }

    if let Some(pattern_props) = &self.obj.pattern_properties {
      let mut compiled = Vec::new();
      for (k, v) in pattern_props {
        if let Ok(re) = regex::Regex::new(k) {
          compiled.push((crate::database::schema::CompiledRegex(re), v.clone()));
        }
      }
      if !compiled.is_empty() {
        self.obj.compiled_pattern_properties = Some(compiled);
      }
    }
  }

  pub fn harvest(&mut self, to_insert: &mut Vec<(String, Schema)>) {
    if let Some(id) = &self.obj.id {
      to_insert.push((id.clone(), self.clone()));
    }
    self.map_children(|child| child.harvest(to_insert));
  }

  pub fn map_children<F>(&mut self, mut f: F)
  where
    F: FnMut(&mut Schema),
  {
    if let Some(props) = &mut self.obj.properties {
      for v in props.values_mut() {
        let mut inner = (**v).clone();
        f(&mut inner);
        *v = Arc::new(inner);
      }
    }

    if let Some(pattern_props) = &mut self.obj.pattern_properties {
      for v in pattern_props.values_mut() {
        let mut inner = (**v).clone();
        f(&mut inner);
        *v = Arc::new(inner);
      }
    }

    let mut map_arr = |arr: &mut Vec<Arc<Schema>>| {
      for v in arr.iter_mut() {
        let mut inner = (**v).clone();
        f(&mut inner);
        *v = Arc::new(inner);
      }
    };

    if let Some(arr) = &mut self.obj.prefix_items {
      map_arr(arr);
    }
    if let Some(arr) = &mut self.obj.all_of {
      map_arr(arr);
    }
    if let Some(arr) = &mut self.obj.one_of {
      map_arr(arr);
    }

    let mut map_opt = |opt: &mut Option<Arc<Schema>>| {
      if let Some(v) = opt {
        let mut inner = (**v).clone();
        f(&mut inner);
        *v = Arc::new(inner);
      }
    };

    map_opt(&mut self.obj.additional_properties);
    map_opt(&mut self.obj.items);
    map_opt(&mut self.obj.contains);
    map_opt(&mut self.obj.property_names);
    map_opt(&mut self.obj.not);
    map_opt(&mut self.obj.if_);
    map_opt(&mut self.obj.then_);
    map_opt(&mut self.obj.else_);
  }
}

impl<'de> Deserialize<'de> for Schema {
  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  where
    D: serde::Deserializer<'de>,
  {
    let v: Value = Deserialize::deserialize(deserializer)?;

    if let Some(b) = v.as_bool() {
      let mut obj = SchemaObject::default();
      if b {
        obj.extensible = Some(true);
      }
      return Ok(Schema {
        obj,
        always_fail: !b,
      });
    }
    let mut obj: SchemaObject =
      serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?;

    // If a schema is effectively empty (except for potentially carrying an ID),
    // it functions as a boolean `true` schema in Draft2020 which means it should not
    // restrict additional properties natively
    let is_empty = obj.type_.is_none()
      && obj.properties.is_none()
      && obj.pattern_properties.is_none()
      && obj.additional_properties.is_none()
      && obj.required.is_none()
      && obj.dependencies.is_none()
      && obj.items.is_none()
      && obj.prefix_items.is_none()
      && obj.contains.is_none()
      && obj.format.is_none()
      && obj.enum_.is_none()
      && obj.const_.is_none()
      && obj.all_of.is_none()
      && obj.one_of.is_none()
      && obj.not.is_none()
      && obj.if_.is_none()
      && obj.then_.is_none()
      && obj.else_.is_none()
      && obj.r#ref.is_none()
      && obj.family.is_none();

    if is_empty && obj.extensible.is_none() {
      obj.extensible = Some(true);
    }

    Ok(Schema {
      obj,
      always_fail: false,
    })
  }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SchemaTypeOrArray {
  Single(String),
  Multiple(Vec<String>),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Action {
  pub navigate: Option<String>,
  pub punc: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Dependency {
  Props(Vec<String>),
  Schema(Arc<Schema>),
}
