use std::{borrow::Cow, collections::BTreeMap, fmt::Debug};
use serde::{self, Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct TypeRoot {
    #[serde(rename = "f")]
    pub file: String,
    #[serde(rename = "l")]
    pub line: u32,
    #[serde(rename = "i")]
    pub inner: Named<ContainerFormat>,
    #[serde(rename = "e")]
    pub extras: Vec<Named<ContainerFormat>>,
}
#[derive(Serialize, Deserialize)]
pub struct Spanned<T> {
    #[serde(rename = "$")]
    pub value: T,
    #[serde(rename = "_")]
    #[serde(skip_serializing_if = "is_null_bytes", default)]
    pub bytes: (usize, usize),
}
fn is_null_bytes(value: &(usize, usize)) -> bool {
    value.0 == 0 && value.1 == 0
}
impl<T: Debug> Debug for Spanned<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("#{:?} ", self.bytes))?;
        self.value.fmt(f)
    }
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Format {
    Incomplete {
        debug: String,
    },
    TypeName {
        ident: String,
        generics: Vec<Format>,
    },
    Unit,
    Bool,
    I8,
    I16,
    I32,
    I64,
    I128,
    ISIZE,
    U8,
    U16,
    U32,
    U64,
    U128,
    USIZE,
    F32,
    F64,
    Char,
    Str,
    Bytes,
    Option(Box<Format>),
    Never,
    Seq(Box<Format>),
    Map {
        key: Box<Format>,
        value: Box<Format>,
    },
    Tuple(Vec<Format>),
    TupleArray {
        content: Box<Format>,
        size: usize,
    },
}
impl Format {
    pub fn is_primitive(&self) -> bool {
        match self {
            Format::Unit
            | Format::Bool
            | Format::I8
            | Format::I16
            | Format::I32
            | Format::I64
            | Format::I128
            | Format::ISIZE
            | Format::U8
            | Format::U16
            | Format::U32
            | Format::U64
            | Format::U128
            | Format::USIZE
            | Format::F32
            | Format::F64
            | Format::Char
            | Format::Str
            | Format::Bytes => true,
            _ => false,
        }
    }
    pub fn is_typename(&self) -> Option<(&str, &[Format])> {
        match self {
            Format::TypeName { ident, generics } => Some((&ident, &generics)),
            _ => None,
        }
    }
    pub fn as_ident(&self) -> Cow<'static, str> {
        Cow::Borrowed(match self {
            Format::Incomplete { debug } => todo!("Unknown ident incomplete: {debug}"),
            Format::TypeName { ident, generics } => {
                return Cow::Owned({
                    let mut buf = format!("{ident}");
                    for gen in generics.iter() {
                        buf.push('_');
                        buf.push_str(&gen.as_ident());
                    }
                    buf
                })
            }
            Format::Unit => "Nil",
            Format::Bool => "Bool",
            Format::I8 => "I8",
            Format::I16 => "I16",
            Format::I32 => "I32",
            Format::I64 => "I64",
            Format::I128 => "I128",
            Format::ISIZE => "ISIZE",
            Format::U8 => "U8",
            Format::U16 => "U16",
            Format::U32 => "U32",
            Format::U64 => "U64",
            Format::U128 => "U128",
            Format::USIZE => "USIZE",
            Format::F32 => "F32",
            Format::F64 => "F64",
            Format::Char => "Char",
            Format::Str => "Str",
            Format::Bytes => "Bytes",
            Format::Option(of) => return Cow::Owned(format!("{}_Option", of.as_ident())),
            Format::Never => "Never",
            Format::Seq(of) => return Cow::Owned(format!("{}_List", of.as_ident())),
            Format::Map { key, value } => {
                return Cow::Owned(format!("{}_{}_Map", key.as_ident(), value.as_ident()))
            }
            Format::Tuple(of) => {
                return Cow::Owned(format!(
                    "{}Tuple",
                    of.iter()
                        .flat_map(|v| [v.as_ident(), Cow::Borrowed("_")])
                        .collect::<String>()
                ))
            }
            Format::TupleArray { content, size } => {
                return Cow::Owned(format!("{}_{}_TupleOf", content.as_ident(), size))
            }
        })
    }
    pub fn replace_incomplete(&mut self, replacement: Format) {
        if let Format::Incomplete { .. } = self {
            *self = replacement;
            return;
        }
        if self.is_primitive() || self.is_typename().is_some() {
            return;
        }
        match (self, replacement) {
            (Format::Option(ref mut original), Format::Option(replacement)) => {
                original.replace_incomplete(*replacement);
            }
            (Format::Seq(ref mut original), Format::Seq(replacement)) => {
                original.replace_incomplete(*replacement);
            }
            (
                Format::Map {
                    ref mut key,
                    ref mut value,
                },
                Format::Map {
                    key: replace_key,
                    value: replace_value,
                },
            ) => {
                key.replace_incomplete(*replace_key);
                value.replace_incomplete(*replace_value);
            }
            (Format::Tuple(ref mut original), Format::Tuple(replacement_vec)) => {
                for (original_item, replacement) in original.iter_mut().zip(replacement_vec) {
                    original_item.replace_incomplete(replacement);
                }
            }
            (
                Format::TupleArray {
                    ref mut content, ..
                },
                Format::TupleArray {
                    content: replacement_content,
                    ..
                },
            ) => {
                content.replace_incomplete(*replacement_content);
            }
            (original, replacement) => {
                panic!("Failed to merge original and replacement:\n{original:#?}\nREPLACEMENT\n{replacement:#?}")
            }
        }
    }
}
#[derive(Serialize, Deserialize, Debug)]
pub enum ContainerFormat {
    UnitStruct,
    NewTypeStruct(Box<Format>),
    TupleStruct(Vec<Format>),
    Struct(Vec<Named<Format>>),
    Enum(BTreeMap<u32, Named<VariantFormat>>),
}
#[derive(Serialize, Deserialize, Debug)]
pub enum VariantFormat {
    Unit,
    NewType(Box<Format>),
    Tuple(Vec<Format>),
    Struct(Vec<Named<Format>>),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Named<T> {
    #[serde(rename = "id")]
    pub rust_ident: Spanned<String>,
    #[serde(rename = "gn")]
    pub rust_generics: Vec<Spanned<String>>,
    #[serde(skip_serializing_if = "Option::is_none", default)]
    #[serde(rename = "docs")]
    pub rust_docs: Option<String>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    #[serde(rename = "sa")]
    pub serde_attrs: Vec<Spanned<(Spanned<String>, Spanned<String>)>>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    #[serde(rename = "sf")]
    pub serde_flags: Vec<Spanned<String>>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    #[serde(rename = "ca")]
    pub codegen_attrs: Vec<Spanned<(Spanned<String>, Spanned<String>)>>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    #[serde(rename = "cf")]
    pub codegen_flags: Vec<Spanned<String>>,
    #[serde(rename = "$")]
    pub value: T,
}
impl<T> Named<T> {
    pub fn builtin(ident: &str, docs: &str, bytes: Option<(usize, usize)>, value: T) -> Self {
        Named {
            rust_ident: Spanned {
                value: ident.to_string(),
                bytes: bytes.unwrap_or_default(),
            },
            rust_generics: Vec::new(),
            rust_docs: {
                let docs = docs.trim();
                if docs.is_empty() {
                    None
                } else {
                    Some(docs.to_string())
                }
            },
            serde_attrs: Vec::new(),
            serde_flags: Vec::new(),
            codegen_attrs: Vec::new(),
            codegen_flags: Vec::new(),
            value,
        }
    }
}
#[derive(Serialize, Deserialize, Debug)]
pub enum EnumRepresentation {
    External,
    Untagged,
    Tagged {
        tag: Spanned<String>,
        content: Option<Spanned<String>>,
    },
}
impl<T> Named<T> {
    pub fn serialize_name(&self) -> &str {
        self.serde_attrs
            .iter()
            .filter_map(|attr| {
                if attr.value.0.value == "rename" {
                    Some(&attr.value.1.value)
                } else {
                    None
                }
            })
            .last()
            .unwrap_or(&self.rust_ident.value)
    }
}