//! Synor Compiler SDK Types
//!
//! Smart contract compilation, optimization, and ABI generation.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Optimization level for WASM compilation.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OptimizationLevel {
    /// No optimization.
    None,
    /// Basic optimizations (stripping only).
    Basic,
    /// Optimize for size (-Os equivalent).
    Size,
    /// Aggressive optimization (-O3 -Os equivalent).
    Aggressive,
}

impl Default for OptimizationLevel {
    fn default() -> Self {
        OptimizationLevel::Size
    }
}

/// Options for stripping WASM sections.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StripOptions {
    pub strip_debug: bool,
    pub strip_producers: bool,
    pub strip_names: bool,
    pub strip_custom: bool,
    pub preserve_sections: Vec<String>,
    pub strip_unused: bool,
}

impl Default for StripOptions {
    fn default() -> Self {
        StripOptions {
            strip_debug: true,
            strip_producers: true,
            strip_names: true,
            strip_custom: true,
            preserve_sections: Vec::new(),
            strip_unused: true,
        }
    }
}

/// Compiler configuration.
#[derive(Clone, Debug)]
pub struct CompilerConfig {
    pub api_key: String,
    pub endpoint: String,
    pub timeout_ms: u64,
    pub retries: u32,
    pub debug: bool,
    pub optimization_level: OptimizationLevel,
    pub strip_options: Option<StripOptions>,
    pub max_contract_size: usize,
    pub use_wasm_opt: bool,
    pub validate: bool,
    pub extract_metadata: bool,
    pub generate_abi: bool,
}

impl CompilerConfig {
    pub fn new(api_key: impl Into<String>) -> Self {
        CompilerConfig {
            api_key: api_key.into(),
            endpoint: "https://compiler.synor.io/v1".to_string(),
            timeout_ms: 60000,
            retries: 3,
            debug: false,
            optimization_level: OptimizationLevel::Size,
            strip_options: None,
            max_contract_size: 256 * 1024,
            use_wasm_opt: true,
            validate: true,
            extract_metadata: true,
            generate_abi: true,
        }
    }

    pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
        self.endpoint = endpoint.into();
        self
    }

    pub fn with_optimization_level(mut self, level: OptimizationLevel) -> Self {
        self.optimization_level = level;
        self
    }
}

/// Compilation request.
#[derive(Clone, Debug, Serialize)]
pub struct CompilationRequest {
    pub wasm: String, // Base64 encoded
    #[serde(skip_serializing_if = "Option::is_none")]
    pub optimization_level: Option<OptimizationLevel>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub strip_options: Option<StripOptions>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub use_wasm_opt: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub validate: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub extract_metadata: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generate_abi: Option<bool>,
}

/// Validation error.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValidationError {
    pub code: String,
    pub message: String,
    pub location: Option<String>,
}

/// Validation result.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValidationResult {
    pub valid: bool,
    #[serde(default)]
    pub errors: Vec<ValidationError>,
    #[serde(default)]
    pub warnings: Vec<String>,
    #[serde(default)]
    pub export_count: usize,
    #[serde(default)]
    pub import_count: usize,
    #[serde(default)]
    pub function_count: usize,
    #[serde(default)]
    pub memory_pages: (u32, Option<u32>),
}

/// Contract metadata.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ContractMetadata {
    pub name: Option<String>,
    pub version: Option<String>,
    #[serde(default)]
    pub authors: Vec<String>,
    pub description: Option<String>,
    pub license: Option<String>,
    pub repository: Option<String>,
    pub build_timestamp: Option<u64>,
    pub rust_version: Option<String>,
    pub sdk_version: Option<String>,
    #[serde(default)]
    pub custom: HashMap<String, String>,
}

/// Type information.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum TypeInfo {
    #[serde(rename = "u8")]
    U8,
    #[serde(rename = "u16")]
    U16,
    #[serde(rename = "u32")]
    U32,
    #[serde(rename = "u64")]
    U64,
    #[serde(rename = "u128")]
    U128,
    #[serde(rename = "i8")]
    I8,
    #[serde(rename = "i16")]
    I16,
    #[serde(rename = "i32")]
    I32,
    #[serde(rename = "i64")]
    I64,
    #[serde(rename = "i128")]
    I128,
    #[serde(rename = "bool")]
    Bool,
    #[serde(rename = "string")]
    String,
    #[serde(rename = "bytes")]
    Bytes,
    #[serde(rename = "fixedBytes")]
    FixedBytes { size: usize },
    #[serde(rename = "address")]
    Address,
    #[serde(rename = "hash256")]
    Hash256,
    #[serde(rename = "array")]
    Array { element: Box<TypeInfo> },
    #[serde(rename = "fixedArray")]
    FixedArray { element: Box<TypeInfo>, size: usize },
    #[serde(rename = "option")]
    Option { inner: Box<TypeInfo> },
    #[serde(rename = "tuple")]
    Tuple { elements: Vec<TypeInfo> },
    #[serde(rename = "struct")]
    Struct {
        name: String,
        fields: Vec<(String, TypeInfo)>,
    },
    #[serde(rename = "unknown")]
    Unknown { name: String },
}

impl TypeInfo {
    /// Get the type name as a string.
    pub fn type_name(&self) -> String {
        match self {
            TypeInfo::U8 => "u8".to_string(),
            TypeInfo::U16 => "u16".to_string(),
            TypeInfo::U32 => "u32".to_string(),
            TypeInfo::U64 => "u64".to_string(),
            TypeInfo::U128 => "u128".to_string(),
            TypeInfo::I8 => "i8".to_string(),
            TypeInfo::I16 => "i16".to_string(),
            TypeInfo::I32 => "i32".to_string(),
            TypeInfo::I64 => "i64".to_string(),
            TypeInfo::I128 => "i128".to_string(),
            TypeInfo::Bool => "bool".to_string(),
            TypeInfo::String => "String".to_string(),
            TypeInfo::Bytes => "Vec<u8>".to_string(),
            TypeInfo::FixedBytes { size } => format!("[u8; {}]", size),
            TypeInfo::Address => "Address".to_string(),
            TypeInfo::Hash256 => "Hash256".to_string(),
            TypeInfo::Array { element } => format!("Vec<{}>", element.type_name()),
            TypeInfo::FixedArray { element, size } => {
                format!("[{}; {}]", element.type_name(), size)
            }
            TypeInfo::Option { inner } => format!("Option<{}>", inner.type_name()),
            TypeInfo::Tuple { elements } => {
                let names: Vec<_> = elements.iter().map(|e| e.type_name()).collect();
                format!("({})", names.join(", "))
            }
            TypeInfo::Struct { name, .. } => name.clone(),
            TypeInfo::Unknown { name } => name.clone(),
        }
    }
}

/// Parameter ABI.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ParamAbi {
    pub name: String,
    #[serde(rename = "type")]
    pub ty: TypeInfo,
    #[serde(default)]
    pub indexed: bool,
}

/// Function ABI.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionAbi {
    pub name: String,
    pub selector: String,
    #[serde(default)]
    pub inputs: Vec<ParamAbi>,
    #[serde(default)]
    pub outputs: Vec<ParamAbi>,
    #[serde(default)]
    pub view: bool,
    #[serde(default)]
    pub payable: bool,
    pub doc: Option<String>,
}

/// Event ABI.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EventAbi {
    pub name: String,
    pub topic: String,
    #[serde(default)]
    pub params: Vec<ParamAbi>,
    pub doc: Option<String>,
}

/// Error ABI.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ErrorAbi {
    pub name: String,
    pub selector: String,
    #[serde(default)]
    pub params: Vec<ParamAbi>,
    pub doc: Option<String>,
}

/// Contract ABI (Application Binary Interface).
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ContractAbi {
    pub name: String,
    pub version: String,
    #[serde(default)]
    pub functions: Vec<FunctionAbi>,
    #[serde(default)]
    pub events: Vec<EventAbi>,
    #[serde(default)]
    pub errors: Vec<ErrorAbi>,
}

impl ContractAbi {
    /// Find a function by name.
    pub fn find_function(&self, name: &str) -> Option<&FunctionAbi> {
        self.functions.iter().find(|f| f.name == name)
    }

    /// Find a function by selector.
    pub fn find_by_selector(&self, selector: &str) -> Option<&FunctionAbi> {
        self.functions.iter().find(|f| f.selector == selector)
    }

    /// Serialize to JSON.
    pub fn to_json(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string_pretty(self)
    }

    /// Deserialize from JSON.
    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
        serde_json::from_str(json)
    }
}

/// Compilation result.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CompilationResult {
    pub contract_id: String,
    pub code: String, // Base64 encoded
    pub code_hash: String,
    pub original_size: usize,
    pub optimized_size: usize,
    pub size_reduction: f64,
    pub metadata: Option<ContractMetadata>,
    pub abi: Option<ContractAbi>,
    #[serde(default)]
    pub estimated_deploy_gas: u64,
    pub validation: Option<ValidationResult>,
    #[serde(default)]
    pub warnings: Vec<String>,
}

impl CompilationResult {
    /// Returns size statistics as a formatted string.
    pub fn size_stats(&self) -> String {
        format!(
            "Original: {} bytes, Optimized: {} bytes, Reduction: {:.1}%",
            self.original_size, self.optimized_size, self.size_reduction
        )
    }

    /// Decode the code from base64.
    pub fn decode_code(&self) -> Result<Vec<u8>, base64::DecodeError> {
        use base64::Engine;
        base64::engine::general_purpose::STANDARD.decode(&self.code)
    }
}

/// Source mapping entry.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SourceMapping {
    pub wasm_offset: usize,
    pub line: usize,
    pub column: usize,
    pub function: Option<String>,
}

/// Source map for debugging.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SourceMap {
    pub file: String,
    #[serde(default)]
    pub mappings: Vec<SourceMapping>,
}

/// Size breakdown.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SizeBreakdown {
    #[serde(default)]
    pub code: usize,
    #[serde(default)]
    pub data: usize,
    #[serde(default)]
    pub types: usize,
    #[serde(default)]
    pub functions: usize,
    #[serde(default)]
    pub memory: usize,
    #[serde(default)]
    pub table: usize,
    #[serde(default)]
    pub exports: usize,
    #[serde(default)]
    pub imports: usize,
    #[serde(default)]
    pub custom: usize,
    #[serde(default)]
    pub total: usize,
}

/// Function analysis.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionAnalysis {
    pub name: String,
    pub size: usize,
    pub instruction_count: usize,
    pub local_count: usize,
    pub exported: bool,
    pub estimated_gas: u64,
}

/// Import analysis.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ImportAnalysis {
    pub module: String,
    pub name: String,
    pub kind: String, // function, memory, table, global
    pub signature: Option<String>,
}

/// Security issue.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SecurityIssue {
    pub severity: String, // low, medium, high, critical
    #[serde(rename = "type")]
    pub issue_type: String,
    pub description: String,
    pub location: Option<String>,
}

/// Security analysis.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SecurityAnalysis {
    pub score: u8, // 0-100
    #[serde(default)]
    pub issues: Vec<SecurityIssue>,
    #[serde(default)]
    pub recommendations: Vec<String>,
}

/// Gas analysis.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GasAnalysis {
    pub deployment_gas: u64,
    #[serde(default)]
    pub function_gas: HashMap<String, u64>,
    #[serde(default)]
    pub memory_init_gas: u64,
    #[serde(default)]
    pub data_section_gas: u64,
}

/// Contract analysis result.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ContractAnalysis {
    pub size_breakdown: SizeBreakdown,
    #[serde(default)]
    pub functions: Vec<FunctionAnalysis>,
    #[serde(default)]
    pub imports: Vec<ImportAnalysis>,
    pub security: Option<SecurityAnalysis>,
    pub gas_analysis: Option<GasAnalysis>,
}

/// Compiler error.
#[derive(Debug)]
pub struct CompilerError {
    pub message: String,
    pub code: Option<String>,
    pub http_status: Option<u16>,
}

impl std::fmt::Display for CompilerError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(code) = &self.code {
            write!(f, "{} ({})", self.message, code)
        } else {
            write!(f, "{}", self.message)
        }
    }
}

impl std::error::Error for CompilerError {}

/// Constants
pub const MAX_CONTRACT_SIZE: usize = 256 * 1024; // 256 KB
pub const MAX_MEMORY_PAGES: u32 = 16;
