//! Leak-based [`Deserialize`] for [`Typ`].
//!
//! # Warning
//!
//! Every call to `deserialize` leaks memory proportional to the size of the
//! decoded schema (strings and field arrays become `&'static` via [`Box::leak`]).
//! Safe for CLI tools and short-lived clients that deserialize a bounded set
//! of schemas per process. **Do not use in long-running servers that parse
//! untrusted or unbounded JSON schemas.**

use serde::Deserialize;

use crate::{EnumType, Fields, ScalarTyp, SimpleEnumType, StructType, Typ};

type NamedField = (&'static str, Typ);

fn leak_str(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

fn leak_slice<T>(v: Vec<T>) -> &'static [T] {
    Box::leak(v.into_boxed_slice())
}

#[derive(Deserialize)]
#[serde(tag = "type", content = "data")]
enum TypWire {
    Bool,
    U8,
    U16,
    U32,
    U64,
    I32,
    I64,
    F32,
    F64,
    Str,
    Datetime,
    Timestamp,
    Decimal,
    Id32,
    Id64,
    Fuid,
    LowId,
    Bytes,
    ArrayBytes(u32),
    Array(u32, Box<TypWire>),
    Vec(Box<TypWire>),
    Optional(Box<TypWire>),
    SimpleEnum(SimpleEnumWire),
    Struct(StructWire),
    Enum(EnumWire),
    Void,
    Json,
    JsonBytes,
    Custom(String, Vec<TypWire>),
}

#[derive(Deserialize)]
struct StructWire {
    name: String,
    fields: FieldsWire,
}

#[derive(Deserialize)]
#[serde(tag = "type", content = "data")]
enum FieldsWire {
    Named(Vec<(String, TypWire)>),
    Unnamed(Vec<TypWire>),
}

#[derive(Deserialize)]
struct EnumWire {
    name: String,
    variants: serde_json::Map<String, serde_json::Value>,
}

#[derive(Deserialize)]
struct SimpleEnumWire {
    name: String,
    variants: serde_json::Map<String, serde_json::Value>,
}

fn wire_to_typ(w: TypWire) -> Result<Typ, String> {
    match w {
        TypWire::Bool => Ok(Typ::Scalar(ScalarTyp::Bool)),
        TypWire::U8 => Ok(Typ::Scalar(ScalarTyp::U8)),
        TypWire::U16 => Ok(Typ::Scalar(ScalarTyp::U16)),
        TypWire::U32 => Ok(Typ::Scalar(ScalarTyp::U32)),
        TypWire::U64 => Ok(Typ::Scalar(ScalarTyp::U64)),
        TypWire::I32 => Ok(Typ::Scalar(ScalarTyp::I32)),
        TypWire::I64 => Ok(Typ::Scalar(ScalarTyp::I64)),
        TypWire::F32 => Ok(Typ::Scalar(ScalarTyp::F32)),
        TypWire::F64 => Ok(Typ::Scalar(ScalarTyp::F64)),
        TypWire::Str => Ok(Typ::Scalar(ScalarTyp::Str)),
        TypWire::Datetime => Ok(Typ::Scalar(ScalarTyp::Datetime)),
        TypWire::Timestamp => Ok(Typ::Scalar(ScalarTyp::Timestamp)),
        TypWire::Decimal => Ok(Typ::Scalar(ScalarTyp::Decimal)),
        TypWire::Id32 => Ok(Typ::Scalar(ScalarTyp::Id32)),
        TypWire::Id64 => Ok(Typ::Scalar(ScalarTyp::Id64)),
        TypWire::Fuid => Ok(Typ::Scalar(ScalarTyp::Fuid)),
        TypWire::LowId => Ok(Typ::Scalar(ScalarTyp::LowId)),
        TypWire::Bytes => Ok(Typ::Scalar(ScalarTyp::Bytes)),
        TypWire::ArrayBytes(n) => Ok(Typ::Scalar(ScalarTyp::ArrayBytes(n))),
        TypWire::Array(n, inner) => Ok(Typ::Array(n, Box::leak(Box::new(wire_to_typ(*inner)?)))),
        TypWire::Vec(inner) => Ok(Typ::Vec(Box::leak(Box::new(wire_to_typ(*inner)?)))),
        TypWire::Optional(inner) => Ok(Typ::Optional(Box::leak(Box::new(wire_to_typ(*inner)?)))),
        TypWire::SimpleEnum(SimpleEnumWire { name, variants }) => {
            let v: Vec<(u8, &'static str)> = variants
                .into_iter()
                .map(|(k, value)| {
                    let tag: u8 = k
                        .parse()
                        .map_err(|_| format!("SimpleEnum variant key must be u8: {k:?}"))?;
                    let name: &'static str = match value {
                        serde_json::Value::String(s) => leak_str(s),
                        other => {
                            return Err(format!(
                                "SimpleEnum variant value must be string, got {other:?}"
                            ));
                        }
                    };
                    Ok((tag, name))
                })
                .collect::<Result<Vec<_>, String>>()?;
            Ok(Typ::SimpleEnum(SimpleEnumType {
                name: leak_str(name),
                variants: leak_slice(v),
            }))
        }
        TypWire::Struct(StructWire { name, fields }) => Ok(Typ::Struct(StructType {
            name: leak_str(name),
            fields: wire_to_fields(fields)?,
        })),
        TypWire::Enum(EnumWire { name, variants }) => {
            let v: Vec<(u8, NamedField)> = variants
                .into_iter()
                .map(|(k, value)| {
                    let tag: u8 = k
                        .parse()
                        .map_err(|_| format!("Enum variant key must be u8: {k:?}"))?;
                    let pair: (String, TypWire) = serde_json::from_value(value)
                        .map_err(|e| format!("Enum variant value shape: {e}"))?;
                    Ok((tag, (leak_str(pair.0), wire_to_typ(pair.1)?)))
                })
                .collect::<Result<Vec<_>, String>>()?;
            Ok(Typ::Enum(EnumType {
                name: leak_str(name),
                variants: leak_slice(v),
            }))
        }
        TypWire::Void => Ok(Typ::Scalar(ScalarTyp::Void)),
        TypWire::Json => Ok(Typ::Scalar(ScalarTyp::RustJson)),
        TypWire::JsonBytes => Ok(Typ::Scalar(ScalarTyp::JsonBytes)),
        TypWire::Custom(name, args) => {
            let args: Vec<Typ> = args
                .into_iter()
                .map(wire_to_typ)
                .collect::<Result<Vec<_>, String>>()?;
            Ok(Typ::Custom(leak_str(name), leak_slice(args)))
        }
    }
}

fn wire_to_fields(f: FieldsWire) -> Result<Fields, String> {
    match f {
        FieldsWire::Named(v) => {
            let arr: Vec<NamedField> = v
                .into_iter()
                .map(|(n, t)| Ok((leak_str(n), wire_to_typ(t)?)))
                .collect::<Result<Vec<_>, String>>()?;
            Ok(Fields::Named(leak_slice(arr)))
        }
        FieldsWire::Unnamed(v) => {
            let arr: Vec<Typ> = v
                .into_iter()
                .map(wire_to_typ)
                .collect::<Result<Vec<_>, String>>()?;
            Ok(Fields::Unnamed(leak_slice(arr)))
        }
    }
}

impl<'de> Deserialize<'de> for Typ {
    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        let w = TypWire::deserialize(d)?;
        wire_to_typ(w).map_err(serde::de::Error::custom)
    }
}
