//! Type definitions for HNSW index
//!
//! Contains all public types, enums, and error definitions.

use std::cmp::Ordering;
use std::collections::HashMap;
use std::io;

use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize};

use crate::types::HNSWConfig;
use crate::vector::quantizer::{ProductQuantizer, PQConfig};

// ============================================================================
// P0-3: Transaction Types
// ============================================================================

/// Operation type for transaction logging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Operation {
    /// Insert a new vector
    Insert {
        external_id: String,
        internal_id: usize,
        vector: Vec<f32>,
    },
    /// Soft delete a vector
    Delete {
        external_id: String,
        internal_id: usize,
    },
    /// Restore a soft-deleted vector
    Restore {
        external_id: String,
        internal_id: usize,
    },
}

/// Transaction state
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransactionState {
    /// Transaction is active and accepting operations
    Active,
    /// Transaction has been committed
    Committed,
    /// Transaction has been rolled back
    RolledBack,
}

/// A transaction for atomic operations on the HNSW index
/// 
/// Transactions provide:
/// - Atomic operations: all operations succeed or all are rolled back
/// - Isolation: changes are not visible until commit
/// - Rollback: ability to undo all operations in the transaction
pub struct Transaction {
    /// Unique transaction ID
    pub(crate) id: u64,
    /// Operations performed in this transaction
    pub(crate) operations: Vec<Operation>,
    /// Current state
    pub(crate) state: TransactionState,
}

impl Transaction {
    pub(crate) fn new(id: u64) -> Self {
        Self {
            id,
            operations: Vec::new(),
            state: TransactionState::Active,
        }
    }
    
    /// Get the transaction ID
    pub fn id(&self) -> u64 {
        self.id
    }
    
    /// Get the current state
    pub fn state(&self) -> TransactionState {
        self.state
    }
    
    /// Get the number of operations
    pub fn operation_count(&self) -> usize {
        self.operations.len()
    }
    
    /// Check if transaction is active
    pub fn is_active(&self) -> bool {
        self.state == TransactionState::Active
    }
}

/// Savepoint for partial rollback within a transaction
#[derive(Debug, Clone)]
pub struct Savepoint {
    /// Savepoint name
    pub name: String,
    /// Number of operations at the time of savepoint
    pub(crate) operation_index: usize,
}

// ============================================================================
// P1-1: PQ Integration Types
// ============================================================================

/// Configuration for PQ-enabled HNSW
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PQHNSWConfig {
    /// Base HNSW config
    pub hnsw: HNSWConfig,
    /// PQ config (None = no compression)
    pub pq: Option<PQConfig>,
    /// Whether to store original vectors (for reranking)
    pub store_original: bool,
    /// Number of candidates to rerank with original vectors
    pub rerank_k: usize,
}

impl Default for PQHNSWConfig {
    fn default() -> Self {
        Self {
            hnsw: HNSWConfig::default(),
            pq: None,
            store_original: true,
            rerank_k: 100,
        }
    }
}

impl PQHNSWConfig {
    /// Create config with PQ enabled for memory compression
    pub fn with_pq(dimension: usize) -> Self {
        Self {
            hnsw: HNSWConfig::new(dimension),
            pq: Some(PQConfig::for_dimension(dimension)),
            store_original: false, // Don't store originals to save memory
            rerank_k: 100,
        }
    }
    
    /// Create config with PQ and reranking for better accuracy
    pub fn with_pq_rerank(dimension: usize, rerank_k: usize) -> Self {
        Self {
            hnsw: HNSWConfig::new(dimension),
            pq: Some(PQConfig::for_dimension(dimension)),
            store_original: true,
            rerank_k,
        }
    }
}

// ============================================================================
// Neighbor Types (internal)
// ============================================================================

/// A neighbor candidate with distance (min-heap)
#[derive(Debug, Clone)]
pub(crate) struct Neighbor {
    pub id: usize,
    pub distance: OrderedFloat<f32>,
}

impl PartialEq for Neighbor {
    fn eq(&self, other: &Self) -> bool {
        self.distance == other.distance
    }
}

impl Eq for Neighbor {}

impl PartialOrd for Neighbor {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Neighbor {
    fn cmp(&self, other: &Self) -> Ordering {
        // Reverse ordering for min-heap behavior
        other.distance.cmp(&self.distance)
    }
}

/// Max-heap neighbor (for candidate lists where we want to pop furthest)
#[derive(Debug, Clone)]
pub(crate) struct MaxNeighbor {
    pub id: usize,
    pub distance: OrderedFloat<f32>,
}

impl PartialEq for MaxNeighbor {
    fn eq(&self, other: &Self) -> bool {
        self.distance == other.distance
    }
}

impl Eq for MaxNeighbor {}

impl PartialOrd for MaxNeighbor {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for MaxNeighbor {
    fn cmp(&self, other: &Self) -> Ordering {
        self.distance.cmp(&other.distance)
    }
}

// ============================================================================
// Search Result
// ============================================================================

/// Search result with ID and distance
#[derive(Debug, Clone)]
pub struct SearchResult {
    /// Internal node ID
    pub id: usize,
    /// Distance to query vector
    pub distance: f32,
}

// ============================================================================
// Memory Stats
// ============================================================================

/// Memory usage statistics
#[derive(Debug, Clone)]
pub struct MemoryStats {
    /// Number of nodes in the index
    pub node_count: usize,
    /// Bytes used by original vectors
    pub original_vectors_bytes: usize,
    /// Bytes used by PQ codes
    pub pq_codes_bytes: usize,
    /// Compression ratio (original / compressed)
    pub compression_ratio: f32,
    /// Whether PQ is trained
    pub pq_trained: bool,
}

// ============================================================================
// Persistence Types
// ============================================================================

/// Serializable representation of the HNSW index for persistence
#[derive(Debug, Serialize, Deserialize)]
pub struct HNSWPersisted {
    /// Configuration
    pub config: HNSWConfig,
    /// All nodes
    pub nodes: Vec<super::node::HNSWNode>,
    /// Entry point node ID
    pub entry_point: Option<usize>,
    /// Current maximum level
    pub max_level: usize,
    /// External ID to internal ID mapping
    pub id_map: HashMap<String, usize>,
    /// Internal ID to external ID mapping
    pub reverse_id_map: Vec<String>,
    /// Level multiplier
    pub level_mult: f64,
    /// Random seed
    pub seed: u64,
    /// Set of deleted internal node IDs (P0-2: soft delete)
    #[serde(default)]
    pub deleted: std::collections::HashSet<usize>,
    /// PQ quantizer (P1-1: optional compression)
    #[serde(default)]
    pub quantizer: Option<ProductQuantizer>,
    /// PQ codes for each node (P1-1: compressed vectors)
    #[serde(default)]
    pub pq_codes: Vec<Vec<u8>>,
    /// Whether original vectors are stored
    #[serde(default = "default_store_original")]
    pub store_original: bool,
}

pub(crate) fn default_store_original() -> bool {
    true
}

// ============================================================================
// Error Types
// ============================================================================

/// HNSW errors
#[derive(Debug, thiserror::Error)]
pub enum HNSWError {
    #[error("Vector dimension mismatch: expected {expected}, got {got}")]
    DimensionMismatch { expected: usize, got: usize },
    
    #[error("Duplicate ID: {0}")]
    DuplicateId(String),
    
    #[error("Index capacity exceeded")]
    CapacityExceeded,
    
    #[error("ID not found: {0}")]
    IdNotFound(String),
    
    #[error("IO error: {0}")]
    Io(#[from] io::Error),
    
    #[error("Serialization error: {0}")]
    Serialization(String),
    
    // P0-3: Transaction errors
    #[error("Transaction already active")]
    TransactionActive,
    
    #[error("No active transaction")]
    NoActiveTransaction,
    
    #[error("Transaction not active")]
    TransactionNotActive,
    
    #[error("Savepoint not found: {0}")]
    SavepointNotFound(String),
    
    // P1-1: PQ errors
    #[error("PQ not enabled")]
    PQNotEnabled,
    
    #[error("PQ already trained")]
    PQAlreadyTrained,
    
    #[error("PQ training error: {0}")]
    PQTrainingError(String),
}
