use serde::{Deserialize, Serialize};

use crate::{Address, ControlFlowGraph};

/// A function entry point and display name from a Ghidra program listing.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FunctionDescriptor {
    pub entry: Address,
    pub name: String,
}

/// A function descriptor paired with its basic-block control-flow graph.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FunctionControlFlowGraph {
    pub function: FunctionDescriptor,
    pub graph: ControlFlowGraph,
}

/// Function-local facts collected from listing, symbol, reference, and source-map APIs.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FunctionSummary {
    pub function: FunctionDescriptor,
    pub basic_block_count: u64,
    pub instruction_count: u64,
    #[serde(default)]
    pub callers: Vec<Address>,
    #[serde(default)]
    pub callees: Vec<Address>,
    #[serde(default)]
    pub strings: Vec<String>,
    #[serde(default)]
    pub constants: Vec<String>,
    #[serde(default)]
    pub data_refs: Vec<Address>,
    #[serde(default)]
    pub imports: Vec<ImportInfo>,
    #[serde(default)]
    pub exports: Vec<ExportInfo>,
    #[serde(default)]
    pub source_map: Option<SourceMapInfo>,
}

/// External symbol referenced by a function.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ImportInfo {
    pub name: String,
    #[serde(default)]
    pub library: Option<String>,
    pub kind: ImportKind,
    #[serde(default)]
    pub address: Option<Address>,
    #[serde(default)]
    pub reference_types: Vec<String>,
}

/// Kind of external symbol referenced by a function.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ImportKind {
    Function,
    Data,
    Unknown,
}

/// Program entry point exported by Ghidra for a function.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ExportInfo {
    pub name: String,
    pub address: Address,
    pub kind: ExportKind,
}

/// Kind of exported program entry point.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ExportKind {
    EntryPoint,
}

/// Best source-file identity Ghidra can associate with a function.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SourceMapInfo {
    pub file_path: String,
    pub function_name: String,
    #[serde(default)]
    pub line_start: Option<u64>,
    #[serde(default)]
    pub line_end: Option<u64>,
}
