//! Formal verification DSL for Synor smart contracts.
//!
//! This crate provides:
//! - A domain-specific language for specifying contract invariants
//! - Symbolic execution engine for exploring all execution paths
//! - SMT solver integration for proving/disproving properties
//! - Automated vulnerability detection

pub mod ast;
pub mod checker;
pub mod error;
pub mod parser;
pub mod prover;
pub mod smt;
pub mod symbolic;

pub use ast::{Annotation, Contract, Expression, Invariant, Property, Specification, Statement};
pub use checker::{CheckResult, PropertyChecker, VulnerabilityReport};
pub use error::{VerifierError, VerifierResult};
pub use parser::SpecParser;
pub use prover::{ProofResult, Prover, ProverConfig};
pub use symbolic::{SymbolicExecutor, SymbolicState, SymbolicValue};

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

/// Verification context containing all contract metadata.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VerificationContext {
    /// Contract name.
    pub contract_name: String,
    /// Contract source code.
    pub source: String,
    /// Parsed specifications.
    pub specs: Vec<Specification>,
    /// State variables and their types.
    pub state_vars: HashMap<String, VarType>,
    /// Function signatures.
    pub functions: HashMap<String, FunctionSig>,
}

/// Variable type in the verification DSL.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum VarType {
    /// Unsigned integer with bit width.
    Uint(u16),
    /// Signed integer with bit width.
    Int(u16),
    /// Boolean.
    Bool,
    /// Address (20 bytes).
    Address,
    /// Fixed-size bytes.
    Bytes(u16),
    /// Dynamic bytes.
    DynBytes,
    /// String.
    String,
    /// Array of type.
    Array(Box<VarType>),
    /// Mapping from key to value type.
    Mapping(Box<VarType>, Box<VarType>),
    /// Struct with named fields.
    Struct(String, Vec<(String, VarType)>),
}

impl VarType {
    /// Returns the bit width of numeric types.
    pub fn bit_width(&self) -> Option<u16> {
        match self {
            VarType::Uint(w) | VarType::Int(w) => Some(*w),
            VarType::Bool => Some(1),
            VarType::Address => Some(160),
            VarType::Bytes(n) => Some(*n * 8),
            _ => None,
        }
    }

    /// Checks if type is numeric.
    pub fn is_numeric(&self) -> bool {
        matches!(self, VarType::Uint(_) | VarType::Int(_))
    }
}

/// Function signature for verification.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionSig {
    /// Function name.
    pub name: String,
    /// Parameter types.
    pub params: Vec<(String, VarType)>,
    /// Return type (None for void).
    pub returns: Option<VarType>,
    /// Visibility.
    pub visibility: Visibility,
    /// State mutability.
    pub mutability: Mutability,
    /// Pre-conditions (requires).
    pub preconditions: Vec<Expression>,
    /// Post-conditions (ensures).
    pub postconditions: Vec<Expression>,
}

/// Function visibility.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Visibility {
    Public,
    External,
    Internal,
    Private,
}

/// Function state mutability.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Mutability {
    Pure,
    View,
    Nonpayable,
    Payable,
}

/// Main verifier interface.
pub struct Verifier {
    /// Prover configuration.
    config: ProverConfig,
    /// Symbolic executor.
    executor: SymbolicExecutor,
    /// Property checker.
    checker: PropertyChecker,
}

impl Verifier {
    /// Creates a new verifier with default configuration.
    pub fn new() -> Self {
        Self::with_config(ProverConfig::default())
    }

    /// Creates a new verifier with custom configuration.
    pub fn with_config(config: ProverConfig) -> Self {
        Self {
            config: config.clone(),
            executor: SymbolicExecutor::new(config.clone()),
            checker: PropertyChecker::new(config),
        }
    }

    /// Get the prover configuration
    pub fn config(&self) -> &ProverConfig {
        &self.config
    }

    /// Get the symbolic executor
    pub fn executor(&self) -> &SymbolicExecutor {
        &self.executor
    }

    /// Verifies a contract against its specifications.
    pub fn verify(&self, ctx: &VerificationContext) -> VerifierResult<VerificationReport> {
        let mut report = VerificationReport::new(&ctx.contract_name);

        // Check each specification
        for spec in &ctx.specs {
            let result = self.verify_spec(ctx, spec)?;
            report.add_result(spec.clone(), result);
        }

        // Run vulnerability detection
        let vulns = self.checker.detect_vulnerabilities(ctx)?;
        report.vulnerabilities = vulns;

        // Compute overall status
        report.compute_status();

        Ok(report)
    }

    /// Verifies a single specification.
    fn verify_spec(
        &self,
        ctx: &VerificationContext,
        spec: &Specification,
    ) -> VerifierResult<SpecResult> {
        match spec {
            Specification::Invariant(inv) => {
                let result = self.checker.check_invariant(ctx, inv)?;
                Ok(SpecResult::Invariant(result))
            }
            Specification::Property(prop) => {
                let result = self.checker.check_property(ctx, prop)?;
                Ok(SpecResult::Property(result))
            }
            Specification::Annotation(ann) => {
                let result = self.checker.check_annotation(ctx, ann)?;
                Ok(SpecResult::Annotation(result))
            }
        }
    }

    /// Parses and verifies a contract from source.
    pub fn verify_source(&self, source: &str) -> VerifierResult<VerificationReport> {
        let ctx = SpecParser::parse_contract(source)?;
        self.verify(&ctx)
    }
}

impl Default for Verifier {
    fn default() -> Self {
        Self::new()
    }
}

/// Result of verifying a specification.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum SpecResult {
    Invariant(CheckResult),
    Property(CheckResult),
    Annotation(CheckResult),
}

impl SpecResult {
    /// Returns true if verification succeeded.
    pub fn is_verified(&self) -> bool {
        match self {
            SpecResult::Invariant(r) | SpecResult::Property(r) | SpecResult::Annotation(r) => {
                r.is_verified()
            }
        }
    }
}

/// Overall verification report.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VerificationReport {
    /// Contract name.
    pub contract_name: String,
    /// Results per specification.
    pub results: Vec<(Specification, SpecResult)>,
    /// Detected vulnerabilities.
    pub vulnerabilities: Vec<VulnerabilityReport>,
    /// Overall status.
    pub status: VerificationStatus,
    /// Time taken (ms).
    pub time_ms: u64,
}

impl VerificationReport {
    fn new(contract_name: &str) -> Self {
        Self {
            contract_name: contract_name.to_string(),
            results: Vec::new(),
            vulnerabilities: Vec::new(),
            status: VerificationStatus::Unknown,
            time_ms: 0,
        }
    }

    fn add_result(&mut self, spec: Specification, result: SpecResult) {
        self.results.push((spec, result));
    }

    fn compute_status(&mut self) {
        let all_verified = self.results.iter().all(|(_, r)| r.is_verified());
        let no_vulns = self.vulnerabilities.iter().all(|v| v.severity < Severity::High);

        self.status = if all_verified && no_vulns {
            VerificationStatus::Verified
        } else if self.vulnerabilities.iter().any(|v| v.severity == Severity::Critical) {
            VerificationStatus::Failed
        } else {
            VerificationStatus::PartiallyVerified
        };
    }

    /// Returns a summary string.
    pub fn summary(&self) -> String {
        let verified = self.results.iter().filter(|(_, r)| r.is_verified()).count();
        let total = self.results.len();
        let vulns = self.vulnerabilities.len();

        format!(
            "Contract: {} | Verified: {}/{} | Vulnerabilities: {} | Status: {:?}",
            self.contract_name, verified, total, vulns, self.status
        )
    }
}

/// Verification status.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum VerificationStatus {
    /// All properties verified.
    Verified,
    /// Some properties verified.
    PartiallyVerified,
    /// Verification failed.
    Failed,
    /// Status unknown (timeout, etc.).
    Unknown,
}

/// Vulnerability severity.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Severity {
    Info,
    Low,
    Medium,
    High,
    Critical,
}

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

    #[test]
    fn test_var_type_bit_width() {
        assert_eq!(VarType::Uint(256).bit_width(), Some(256));
        assert_eq!(VarType::Int(128).bit_width(), Some(128));
        assert_eq!(VarType::Bool.bit_width(), Some(1));
        assert_eq!(VarType::Address.bit_width(), Some(160));
        assert_eq!(VarType::Bytes(32).bit_width(), Some(256));
        assert_eq!(VarType::String.bit_width(), None);
    }

    #[test]
    fn test_var_type_is_numeric() {
        assert!(VarType::Uint(256).is_numeric());
        assert!(VarType::Int(128).is_numeric());
        assert!(!VarType::Bool.is_numeric());
        assert!(!VarType::Address.is_numeric());
    }

    #[test]
    fn test_verification_status() {
        let mut report = VerificationReport::new("TestContract");
        assert_eq!(report.status, VerificationStatus::Unknown);

        report.compute_status();
        assert_eq!(report.status, VerificationStatus::Verified);
    }

    #[test]
    fn test_verifier_creation() {
        let verifier = Verifier::new();
        assert_eq!(verifier.config.timeout_ms, 30000);
    }
}
