use std::fmt;

use serde::{Deserialize, Deserializer, Serialize, de};

/// Program address with address-space, numeric offset, and Ghidra display text.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Address {
    pub space: String,
    #[serde(with = "offset_serde")]
    pub offset: u64,
    pub text: String,
}

impl Address {
    /// Returns the Ghidra display form of this address.
    pub fn as_str(&self) -> &str {
        &self.text
    }
}

impl fmt::Display for Address {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(&self.text)
    }
}

/// Address range reported by Ghidra.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AddressRange {
    pub start: Address,
    pub end: Address,
}

/// Symbol-table entry.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SymbolInfo {
    pub name: String,
    pub address: Address,
    pub kind: SymbolKind,
    pub source: SymbolSource,
    pub namespace: String,
    pub primary: bool,
    pub external: bool,
}

/// Ghidra symbol kind.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SymbolKind {
    Function,
    Label,
    Namespace,
    Class,
    Library,
    Parameter,
    LocalVariable,
    GlobalVariable,
    Unknown,
}

/// Source of a Ghidra symbol.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SymbolSource {
    UserDefined,
    Imported,
    Analysis,
    Default,
    Unknown,
}

/// Reference from one address to another.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ReferenceInfo {
    pub from: Address,
    pub to: Address,
    pub reference_type: String,
    pub operand_index: i32,
    pub primary: bool,
    pub external: bool,
    pub call: bool,
    pub data: bool,
    pub read: bool,
    pub write: bool,
}

/// Program memory block.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MemoryBlockInfo {
    pub name: String,
    pub range: AddressRange,
    pub size: u64,
    pub initialized: bool,
    pub read: bool,
    pub write: bool,
    pub execute: bool,
    pub volatile: bool,
    pub overlay: bool,
}

/// Result of one decompiler request.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DecompileResult {
    pub status: DecompileStatus,
    #[serde(default)]
    pub error_message: Option<String>,
    #[serde(default)]
    pub c: Option<String>,
    #[serde(default)]
    pub signature: Option<String>,
    #[serde(default)]
    pub prototype: Option<FunctionPrototype>,
    #[serde(default)]
    pub pcode: Option<PcodeSummary>,
    #[serde(default)]
    pub high_pcode: Option<HighPcodeGraph>,
    #[serde(default)]
    pub data_flow: Option<DataFlowGraph>,
    #[serde(default)]
    pub parameters: Vec<HighSymbol>,
    #[serde(default)]
    pub local_symbols: Vec<HighSymbol>,
}

/// Terminal status of a decompiler request.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DecompileStatus {
    Completed,
    Failed,
    TimedOut,
    Cancelled,
    FailedToStart,
}

/// Function prototype recovered by the decompiler.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FunctionPrototype {
    pub return_type: String,
    #[serde(default)]
    pub return_type_id: Option<String>,
    #[serde(default)]
    pub calling_convention: Option<String>,
    pub parameter_count: u64,
    pub varargs: bool,
    pub inline: bool,
    pub no_return: bool,
    pub has_this_pointer: bool,
}

/// Counts and aggregate P-code facts for a decompiled function.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PcodeSummary {
    pub op_count: u64,
    #[serde(default)]
    pub op_counts: Vec<NamedCount>,
    pub basic_block_count: u64,
    pub varnode_count: u64,
    #[serde(default)]
    pub varnode_space_counts: Vec<NamedCount>,
}

/// High P-code blocks, edges, and operations for a function.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct HighPcodeGraph {
    #[serde(default)]
    pub blocks: Vec<HighPcodeBlock>,
    #[serde(default)]
    pub edges: Vec<ControlFlowEdgeInfo>,
    #[serde(default)]
    pub ops: Vec<PcodeOpInfo>,
}

/// High P-code basic block.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct HighPcodeBlock {
    pub id: String,
    pub index: i32,
    pub block_type: String,
    #[serde(default)]
    pub range: Option<AddressRange>,
    #[serde(default)]
    pub op_sequence_numbers: Vec<String>,
}

/// Data-flow graph derived from high P-code.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DataFlowGraph {
    #[serde(default)]
    pub ops: Vec<DataFlowOp>,
    #[serde(default)]
    pub varnodes: Vec<DataFlowVarnode>,
    #[serde(default)]
    pub variables: Vec<DataFlowVariable>,
    #[serde(default)]
    pub edges: Vec<DataFlowEdge>,
}

/// Data-flow operation node.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DataFlowOp {
    pub id: String,
    pub sequence_number: String,
    pub mnemonic: String,
    pub opcode: i32,
    #[serde(default)]
    pub block_id: Option<String>,
}

/// Data-flow varnode node.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DataFlowVarnode {
    pub id: String,
    pub kind: String,
    #[serde(default)]
    pub variable_id: Option<String>,
    pub varnode: VarnodeInfo,
}

/// Data-flow variable node.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DataFlowVariable {
    pub id: String,
    pub name: String,
    pub data_type: String,
    #[serde(default)]
    pub type_id: Option<String>,
    pub size: u64,
    pub storage: String,
    pub kind: String,
    #[serde(default)]
    pub symbol_id: Option<i64>,
    #[serde(default)]
    pub symbol_address: Option<Address>,
    #[serde(default)]
    pub symbol_type: Option<String>,
    #[serde(default)]
    pub namespace: Option<String>,
}

/// Edge between data-flow nodes.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DataFlowEdge {
    pub source_id: String,
    pub destination_id: String,
    pub kind: String,
    #[serde(default)]
    pub slot: Option<i32>,
}

/// Program-level call graph.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProgramCallGraph {
    #[serde(default)]
    pub nodes: Vec<CallGraphNode>,
    #[serde(default)]
    pub edges: Vec<CallGraphEdge>,
}

/// Program call graph node.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CallGraphNode {
    pub id: String,
    pub kind: String,
    pub name: String,
    #[serde(default)]
    pub address: Option<Address>,
    #[serde(default)]
    pub namespace: Option<String>,
    #[serde(default)]
    pub library: Option<String>,
    #[serde(default)]
    pub function_id: Option<String>,
    #[serde(default)]
    pub symbol_id: Option<String>,
}

/// Program call graph edge.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CallGraphEdge {
    pub id: String,
    pub source_id: String,
    pub destination_id: String,
    pub callsite: Address,
    pub reference_type: String,
    pub operand_index: i32,
    pub computed: bool,
    pub thunk_resolved: bool,
}

/// Program-wide symbols, functions, and recursive type metadata.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProgramMetadata {
    #[serde(default)]
    pub symbols: Vec<ProgramSymbol>,
    #[serde(default)]
    pub functions: Vec<ProgramFunction>,
    #[serde(default)]
    pub types: Vec<ProgramType>,
}

/// Program symbol metadata record.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProgramSymbol {
    pub id: String,
    pub name: String,
    pub kind: String,
    pub source: String,
    pub address: Address,
    pub namespace: String,
    pub primary: bool,
    pub external: bool,
    #[serde(default)]
    pub type_id: Option<String>,
}

/// Program function metadata record.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProgramFunction {
    pub id: String,
    pub name: String,
    pub entry: Address,
    pub namespace: String,
    pub external: bool,
    pub thunk: bool,
    #[serde(default)]
    pub thunked_function_id: Option<String>,
    #[serde(default)]
    pub symbol_id: Option<String>,
    pub signature: ProgramFunctionSignature,
}

/// Program function signature metadata.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProgramFunctionSignature {
    pub display: String,
    pub calling_convention: String,
    #[serde(default)]
    pub return_type_id: Option<String>,
    #[serde(default)]
    pub parameters: Vec<ProgramParameter>,
    pub varargs: bool,
    pub no_return: bool,
}

/// Program function parameter metadata.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProgramParameter {
    pub ordinal: i32,
    pub name: String,
    #[serde(default)]
    pub type_id: Option<String>,
    pub storage: String,
}

/// Recursive program type metadata record.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ProgramType {
    pub id: String,
    pub name: String,
    pub display_name: String,
    pub size: i64,
    pub alignment: i32,
    #[serde(default)]
    pub category_path: Option<String>,
    #[serde(flatten)]
    pub details: ProgramTypeDetails,
}

impl<'de> Deserialize<'de> for ProgramType {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = serde_json::Map::<String, serde_json::Value>::deserialize(deserializer)?;
        validate_program_type_wire_fields(&value).map_err(de::Error::custom)?;
        let wire = ProgramTypeWire::deserialize(serde_json::Value::Object(value))
            .map_err(de::Error::custom)?;
        Ok(Self {
            id: wire.id,
            name: wire.name,
            display_name: wire.display_name,
            size: wire.size,
            alignment: wire.alignment,
            category_path: wire.category_path,
            details: wire.details,
        })
    }
}

#[derive(Deserialize)]
struct ProgramTypeWire {
    id: String,
    name: String,
    display_name: String,
    size: i64,
    alignment: i32,
    #[serde(default)]
    category_path: Option<String>,
    #[serde(flatten)]
    details: ProgramTypeDetails,
}

/// Recursive type variant details.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
pub enum ProgramTypeDetails {
    Builtin,
    Unknown,
    Pointer {
        #[serde(default)]
        pointee_type_id: Option<String>,
    },
    Array {
        #[serde(default)]
        element_type_id: Option<String>,
        element_count: i32,
        element_size: i32,
    },
    Structure {
        #[serde(default)]
        components: Vec<ProgramTypeComponent>,
    },
    Union {
        #[serde(default)]
        components: Vec<ProgramTypeComponent>,
    },
    Enum {
        signed: bool,
        #[serde(default)]
        values: Vec<ProgramEnumValue>,
    },
    Typedef {
        #[serde(default)]
        base_type_id: Option<String>,
    },
    FunctionDefinition {
        signature: ProgramFunctionSignature,
    },
    Bitfield {
        #[serde(default)]
        base_type_id: Option<String>,
        bit_size: i32,
        bit_offset: i32,
        storage_size: i32,
    },
}

/// Structure or union component metadata.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProgramTypeComponent {
    pub ordinal: i32,
    pub name: String,
    pub offset: i32,
    pub length: i32,
    #[serde(default)]
    pub type_id: Option<String>,
    #[serde(default)]
    pub bit_size: Option<i32>,
    #[serde(default)]
    pub bit_offset: Option<i32>,
    #[serde(default)]
    pub comment: Option<String>,
}

/// Enum member metadata.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProgramEnumValue {
    pub name: String,
    pub value: String,
    #[serde(default)]
    pub comment: Option<String>,
}

/// Name/count pair used by aggregate summaries.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct NamedCount {
    pub name: String,
    pub count: u64,
}

/// High-level symbol recovered by the decompiler.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct HighSymbol {
    pub name: String,
    pub data_type: String,
    #[serde(default)]
    pub type_id: Option<String>,
    pub size: u64,
    pub storage: String,
}

/// Listing instruction.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct InstructionInfo {
    pub address: Address,
    pub mnemonic: String,
    #[serde(default)]
    pub pcode: Vec<PcodeOpInfo>,
    #[serde(default)]
    pub operands: Vec<OperandInfo>,
    pub length: u64,
    #[serde(default)]
    pub bytes: Vec<u8>,
    #[serde(default)]
    pub fallthrough: Option<Address>,
    pub flow_type: String,
}

/// Instruction operand.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct OperandInfo {
    pub index: i32,
    pub text: String,
    #[serde(default)]
    pub objects: Vec<String>,
    #[serde(default)]
    pub references: Vec<ReferenceInfo>,
}

/// Defined data item.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DataInfo {
    pub address: Address,
    pub range: AddressRange,
    pub data_type: String,
    pub length: u64,
    #[serde(default)]
    pub value: Option<String>,
    pub display: String,
}

/// Basic-block control-flow graph.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ControlFlowGraph {
    #[serde(default)]
    pub blocks: Vec<BasicBlockInfo>,
    #[serde(default)]
    pub edges: Vec<ControlFlowEdgeInfo>,
}

/// Basic block in a control-flow graph.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BasicBlockInfo {
    pub id: String,
    pub name: String,
    pub range: AddressRange,
    pub flow_type: String,
    #[serde(default)]
    pub instruction_addresses: Vec<Address>,
}

/// Edge between basic blocks.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ControlFlowEdgeInfo {
    pub source_block_id: String,
    pub destination_block_id: String,
    pub flow_type: String,
    #[serde(default)]
    pub source_address: Option<Address>,
    #[serde(default)]
    pub destination_address: Option<Address>,
    #[serde(default)]
    pub reference_address: Option<Address>,
}

/// P-code operation.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PcodeOpInfo {
    pub sequence_number: String,
    pub sequence_target: Address,
    pub sequence_time: i32,
    pub sequence_order: i32,
    pub mnemonic: String,
    pub opcode: i32,
    #[serde(default)]
    pub output: Option<VarnodeInfo>,
    #[serde(default)]
    pub inputs: Vec<VarnodeInfo>,
}

/// P-code varnode.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct VarnodeInfo {
    pub space: String,
    #[serde(with = "offset_serde")]
    pub offset: u64,
    pub size: u64,
    pub text: String,
    #[serde(default)]
    pub address: Option<Address>,
    pub constant: bool,
    pub register: bool,
    pub unique: bool,
    pub address_tied: bool,
    pub input: bool,
    pub persistent: bool,
}

fn validate_program_type_wire_fields(
    value: &serde_json::Map<String, serde_json::Value>,
) -> Result<(), String> {
    let kind = value
        .get("kind")
        .and_then(serde_json::Value::as_str)
        .ok_or_else(|| "types.kind must be present and a string".to_string())?;
    for key in value.keys() {
        if !program_type_field_allowed(kind, key.as_str()) {
            return Err(format!("unknown field `{key}` in program type `{kind}`"));
        }
    }
    Ok(())
}

fn program_type_field_allowed(kind: &str, field: &str) -> bool {
    if matches!(
        field,
        "id" | "name" | "display_name" | "kind" | "size" | "alignment" | "category_path"
    ) {
        return true;
    }
    match kind {
        "builtin" | "unknown" => false,
        "pointer" => field == "pointee_type_id",
        "array" => matches!(field, "element_type_id" | "element_count" | "element_size"),
        "structure" | "union" => field == "components",
        "enum" => matches!(field, "signed" | "values"),
        "typedef" => field == "base_type_id",
        "function_definition" => field == "signature",
        "bitfield" => matches!(
            field,
            "base_type_id" | "bit_size" | "bit_offset" | "storage_size"
        ),
        _ => true,
    }
}

mod offset_serde {
    use std::fmt;

    use serde::{
        Deserializer, Serializer,
        de::{self, Visitor},
    };

    pub fn serialize<S>(offset: &u64, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_u64(*offset)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_any(OffsetVisitor)
    }

    struct OffsetVisitor;

    impl<'de> Visitor<'de> for OffsetVisitor {
        type Value = u64;

        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
            formatter.write_str("an unsigned integer or decimal string")
        }

        fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
            Ok(value)
        }

        fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            u64::try_from(value).map_err(|_| E::custom("offset must not be negative"))
        }

        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            value.parse::<u64>().map_err(E::custom)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Address;

    #[test]
    fn address_offset_accepts_decimal_string() {
        let address: Address = serde_json::from_str(
            r#"{"space":"ram","offset":"18446744073709551615","text":"ram:ffff"}"#,
        )
        .expect("address parses");

        assert_eq!(address.offset, u64::MAX);
        assert_eq!(address.to_string(), "ram:ffff");
    }
}
