
#[cfg(feature = "json-schema")]
pub mod json_schema;

#[cfg(feature = "openapi")]
pub mod openapi;

#[cfg(feature = "graphql")]
pub mod graphql;

#[cfg(feature = "protobuf")]
pub mod protobuf;

#[cfg(feature = "typescript")]
pub mod typescript;

#[cfg(feature = "avro")]
pub mod avro;

pub mod format_ids {
    pub const JSON_SCHEMA: &str = "json-schema";
    pub const OPENAPI: &str = "openapi";
    pub const GRAPHQL: &str = "graphql";
    pub const PROTOBUF: &str = "protobuf";
    pub const TYPESCRIPT: &str = "typescript";
    pub const AVRO: &str = "avro";
}

pub mod utils {
    use crate::types::{PrimitiveType, SchemaType};

    pub fn type_description(schema_type: &SchemaType) -> String {
        match schema_type {
            SchemaType::Primitive(p) => primitive_description(p),
            SchemaType::Option(inner) => format!("Optional {}", type_description(inner)),
            SchemaType::Array(inner) => format!("Array of {}", type_description(inner)),
            SchemaType::Set(inner) => format!("Set of {}", type_description(inner)),
            SchemaType::Map(inner) => format!("Map with {} values", type_description(inner)),
            SchemaType::Tuple(types) => {
                let inner: Vec<_> = types.iter().map(|t| type_description(t)).collect();
                format!("Tuple of ({})", inner.join(", "))
            }
            SchemaType::Struct(_) => "Object".to_string(),
            SchemaType::Enum(_) => "Enum".to_string(),
            SchemaType::Newtype(n) => type_description(&n.inner_type),
            SchemaType::Reference(name) => name.clone(),
            SchemaType::Unit => "Unit".to_string(),
            SchemaType::Any => "Any".to_string(),
        }
    }

    fn primitive_description(p: &PrimitiveType) -> String {
        match p {
            PrimitiveType::Bool => "Boolean".to_string(),
            PrimitiveType::I8 => "8-bit signed integer".to_string(),
            PrimitiveType::I16 => "16-bit signed integer".to_string(),
            PrimitiveType::I32 => "32-bit signed integer".to_string(),
            PrimitiveType::I64 => "64-bit signed integer".to_string(),
            PrimitiveType::I128 => "128-bit signed integer".to_string(),
            PrimitiveType::Isize => "Pointer-sized signed integer".to_string(),
            PrimitiveType::U8 => "8-bit unsigned integer".to_string(),
            PrimitiveType::U16 => "16-bit unsigned integer".to_string(),
            PrimitiveType::U32 => "32-bit unsigned integer".to_string(),
            PrimitiveType::U64 => "64-bit unsigned integer".to_string(),
            PrimitiveType::U128 => "128-bit unsigned integer".to_string(),
            PrimitiveType::Usize => "Pointer-sized unsigned integer".to_string(),
            PrimitiveType::F32 => "32-bit floating point".to_string(),
            PrimitiveType::F64 => "64-bit floating point".to_string(),
            PrimitiveType::Char => "Unicode character".to_string(),
            PrimitiveType::String => "String".to_string(),
            PrimitiveType::Bytes => "Binary data".to_string(),
        }
    }

    pub fn escape_string(s: &str) -> String {
        s.replace('\\', "\\\\")
            .replace('"', "\\\"")
            .replace('\n', "\\n")
            .replace('\r', "\\r")
            .replace('\t', "\\t")
    }

    pub fn sanitize_identifier(name: &str, format: &str) -> String {
        let sanitized: String = name
            .chars()
            .map(|c| if c.is_alphanumeric() || c == '_' { c } else { '_' })
            .collect();

        if sanitized.starts_with(|c: char| c.is_ascii_digit()) {
            format!("_{}", sanitized)
        } else {
            match format {
                "protobuf" => {
                    if is_protobuf_reserved(&sanitized) {
                        format!("{}_", sanitized)
                    } else {
                        sanitized
                    }
                }
                "typescript" => {
                    if is_typescript_reserved(&sanitized) {
                        format!("{}_", sanitized)
                    } else {
                        sanitized
                    }
                }
                "graphql" => {
                    let mut chars = sanitized.chars();
                    match chars.next() {
                        Some(first) => first.to_uppercase().chain(chars).collect(),
                        None => sanitized,
                    }
                }
                _ => sanitized,
            }
        }
    }

    fn is_protobuf_reserved(name: &str) -> bool {
        matches!(
            name,
            "syntax" | "import" | "package" | "option" | "message" | "enum" | "service" | "rpc"
                | "returns" | "repeated" | "optional" | "required" | "map" | "oneof" | "reserved"
                | "extensions" | "extend" | "group" | "true" | "false"
        )
    }

    fn is_typescript_reserved(name: &str) -> bool {
        matches!(
            name,
            "break" | "case" | "catch" | "class" | "const" | "continue" | "debugger" | "default"
                | "delete" | "do" | "else" | "enum" | "export" | "extends" | "false" | "finally"
                | "for" | "function" | "if" | "import" | "in" | "instanceof" | "new" | "null"
                | "return" | "super" | "switch" | "this" | "throw" | "true" | "try" | "typeof"
                | "var" | "void" | "while" | "with" | "yield" | "let" | "static" | "implements"
                | "interface" | "package" | "private" | "protected" | "public" | "any" | "boolean"
                | "number" | "string" | "symbol" | "type" | "from" | "of" | "namespace" | "module"
                | "declare" | "abstract" | "as" | "async" | "await" | "constructor" | "get"
                | "set" | "require"
        )
    }
}
