//! Cost model: estimated costs for each operation.
//!
//! Supports multiple cost models:
//! - `ns` (default): conservative nanosecond estimates for modern x86-64
//! - `cycles`: clock cycle estimates (architecture-independent baseline)
//! - `custom(...)`: user-defined cost weights
//!
//! All models are intentionally pessimistic: better to reject a fast function
//! than to accept a slow one.

/// Cost profile — determines which cost table is used.
#[derive(Debug, Clone, PartialEq, Default)]
pub enum CostProfile {
    /// Nanosecond estimates (default). Pessimistic, based on modern x86-64.
    #[default]
    Nanoseconds,
    /// Clock cycle estimates. More hardware-neutral than nanoseconds.
    Cycles,
    /// Custom cost weights. Every field is user-configurable.
    #[allow(dead_code)]
    Custom(CostTable),
}

/// A complete cost table. Each field is the cost of one operation
/// in whatever unit the user chose (ns, cycles, or arbitrary weight).
#[derive(Debug, Clone, PartialEq)]
pub struct CostTable {
    pub alloc: u64,
    pub syscall: u64,
    pub net_syscall: u64,
    pub mutex_lock: u64,
    pub alu: u64,
    pub call: u64,
    pub branch: u64,
    pub index: u64,
    pub hashmap_lookup: u64,
    pub hashmap_insert: u64,
    pub format: u64,
    pub println: u64,
}

impl CostTable {
    /// Nanosecond cost table — conservative estimates for modern x86-64.
    pub const NS: CostTable = CostTable {
        alloc: 50,
        syscall: 1_000,
        net_syscall: 10_000,
        mutex_lock: 100,
        alu: 1,
        call: 5,
        branch: 1,
        index: 1,
        hashmap_lookup: 25,
        hashmap_insert: 50,
        format: 200,
        println: 5_000,
    };

    /// Clock cycle cost table — architecture-independent baseline.
    /// Based on typical modern superscalar CPU (Zen4 / Alder Lake average).
    pub const CYCLES: CostTable = CostTable {
        alloc: 200,          // ~200 cycles for malloc
        syscall: 3_000,      // ~3k cycles for a syscall
        net_syscall: 30_000, // ~30k cycles for network I/O
        mutex_lock: 300,     // ~300 cycles for uncontended lock
        alu: 1,              // 1 cycle (pipelined)
        call: 15,            // ~15 cycles for non-inlined call
        branch: 3,           // ~3 cycles for branch mispredict amortized
        index: 4,            // ~4 cycles (L1 hit)
        hashmap_lookup: 80,  // ~80 cycles (hash + probe + compare)
        hashmap_insert: 150, // ~150 cycles (hash + probe + alloc amortized)
        format: 600,         // ~600 cycles for format machinery
        println: 15_000,     // ~15k cycles for stdout + format + flush
    };
}

impl CostProfile {
    pub fn table(&self) -> &CostTable {
        match self {
            CostProfile::Nanoseconds => &CostTable::NS,
            CostProfile::Cycles => &CostTable::CYCLES,
            CostProfile::Custom(table) => table,
        }
    }

    pub fn unit_name(&self) -> &str {
        match self {
            CostProfile::Nanoseconds => "ns",
            CostProfile::Cycles => "cycles",
            CostProfile::Custom(_) => "units",
        }
    }
}

// ── Convenience accessors (backward compat) ────────────────────────────────
// These constants preserve the v0.1 public API. New code should use
// CostProfile::table() instead.

/// Cost of a heap allocation (Vec::new, Box::new, String::from, etc.)
#[allow(dead_code)]
pub const ALLOC_NS: u64 = CostTable::NS.alloc;

/// Cost of a syscall (fs::read, write, open, etc.)
#[allow(dead_code)]
pub const SYSCALL_NS: u64 = CostTable::NS.syscall;

/// Cost of a network syscall (TcpStream, UdpSocket, etc.)
#[allow(dead_code)]
pub const NET_SYSCALL_NS: u64 = CostTable::NS.net_syscall;

/// Cost of a mutex lock
#[allow(dead_code)]
pub const MUTEX_LOCK_NS: u64 = CostTable::NS.mutex_lock;

/// Cost of a single arithmetic/logic operation
#[allow(dead_code)]
pub const ALU_NS: u64 = CostTable::NS.alu;

/// Cost of a function call (non-inlined)
#[allow(dead_code)]
pub const CALL_NS: u64 = CostTable::NS.call;

/// Cost of a branch (if/else, match arm)
#[allow(dead_code)]
pub const BRANCH_NS: u64 = CostTable::NS.branch;

/// Cost of an array/slice index
#[allow(dead_code)]
pub const INDEX_NS: u64 = CostTable::NS.index;

/// Cost of a hash map lookup
#[allow(dead_code)]
pub const HASHMAP_LOOKUP_NS: u64 = CostTable::NS.hashmap_lookup;

/// Cost of a hash map insert
#[allow(dead_code)]
pub const HASHMAP_INSERT_NS: u64 = CostTable::NS.hashmap_insert;

/// Cost of a string format operation
#[allow(dead_code)]
pub const FORMAT_NS: u64 = CostTable::NS.format;

/// Cost of a println! or print!
#[allow(dead_code)]
pub const PRINTLN_NS: u64 = CostTable::NS.println;

/// Default stack size estimate per local variable (bytes)
pub const USIZE_SIZE: u64 = 8;

/// Recognized allocating function paths.
pub const ALLOC_PATHS: &[&str] = &[
    "Vec::new",
    "Vec::with_capacity",
    "vec!",
    "Box::new",
    "String::new",
    "String::from",
    "String::with_capacity",
    "format!",
    "to_string",
    "to_owned",
    "clone",
    "collect",
    "HashMap::new",
    "HashSet::new",
    "BTreeMap::new",
    "BTreeSet::new",
    "VecDeque::new",
    "BinaryHeap::new",
    "Rc::new",
    "Arc::new",
];

/// Recognized syscall paths.
pub const SYSCALL_PATHS: &[&str] = &[
    "fs::read",
    "fs::write",
    "fs::read_to_string",
    "fs::create_dir",
    "fs::remove_file",
    "fs::remove_dir",
    "fs::rename",
    "fs::copy",
    "fs::metadata",
    "File::open",
    "File::create",
    "read",
    "write",
    "flush",
    "stdin",
    "stdout",
    "stderr",
];

/// Recognized network paths.
pub const NET_PATHS: &[&str] = &[
    "TcpStream",
    "TcpListener",
    "UdpSocket",
    "connect",
    "bind",
    "listen",
    "accept",
];

// Sync and format patterns are matched directly in analyze.rs
// via method name and macro name checks.
