// Copyright 2025 Stoolap Contributors
// Copyright 2025 Oxibase Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Engine trait for the storage engine
//!

use rustc_hash::FxHashMap;

use crate::core::{IsolationLevel, Result, Row, Schema};
use crate::storage::config::Config;
use crate::storage::traits::{Index, Transaction};

/// Engine represents the storage engine
///
/// This is the main entry point for interacting with the database.
/// It manages transactions, tables, indexes, and persistence.
///
/// # Example
///
/// ```ignore
/// let config = Config::with_path("/tmp/mydb");
/// let mut engine = MvccEngine::new(config);
/// engine.open()?;
///
/// let tx = engine.begin_transaction()?;
/// // ... perform operations ...
/// tx.commit()?;
///
/// engine.close()?;
/// ```
pub trait Engine: Send + Sync {
    /// Opens the storage engine
    ///
    /// This initializes the engine, opens the database path (if any),
    /// recovers from WAL, and loads existing data.
    fn open(&mut self) -> Result<()>;

    /// Closes the storage engine
    ///
    /// This flushes pending writes, creates a final snapshot if needed,
    /// and releases all resources.
    fn close(&mut self) -> Result<()>;

    /// Begins a new transaction
    ///
    /// The transaction will use the engine's default isolation level.
    fn begin_transaction(&self) -> Result<Box<dyn Transaction>>;

    /// Begins a new transaction with a specific isolation level
    ///
    /// # Arguments
    /// * `level` - The isolation level for the transaction
    fn begin_transaction_with_level(&self, level: IsolationLevel) -> Result<Box<dyn Transaction>>;

    /// Returns the path to the database directory
    ///
    /// Returns `None` if operating in memory-only mode.
    fn path(&self) -> Option<&str>;

    /// Checks if a table exists
    fn table_exists(&self, table_name: &str) -> Result<bool>;

    /// Checks if a view exists
    fn view_exists(&self, view_name: &str) -> Result<bool>;

    /// Checks if an index exists
    fn index_exists(&self, index_name: &str, table_name: &str) -> Result<bool>;

    /// Gets an index by name
    fn get_index(&self, table_name: &str, index_name: &str) -> Result<Box<dyn Index>>;

    /// Gets the schema for a table
    fn get_table_schema(&self, table_name: &str) -> Result<Schema>;

    /// Updates the schema for a table
    fn update_table_schema(&self, table_name: &str, schema: Schema) -> Result<()>;

    /// Lists all indexes for a table
    ///
    /// Returns a map from index name to index type string.
    fn list_table_indexes(&self, table_name: &str) -> Result<FxHashMap<String, String>>;

    /// Gets all index objects for a table
    fn get_all_indexes(&self, table_name: &str) -> Result<Vec<std::sync::Arc<dyn Index>>>;

    /// Gets the current default isolation level
    fn get_isolation_level(&self) -> IsolationLevel;

    /// Sets the default isolation level for new transactions
    fn set_isolation_level(&mut self, level: IsolationLevel) -> Result<()>;

    /// Gets the current engine configuration
    ///
    /// Returns a clone of the configuration to avoid lifetime issues with internal locks.
    fn get_config(&self) -> Config;

    /// Updates the engine configuration
    ///
    /// Note: Some configuration changes may require a restart to take effect.
    fn update_config(&mut self, config: Config) -> Result<()>;

    /// Manually triggers snapshot creation for all tables
    ///
    /// This is useful for creating a consistent backup point.
    fn create_snapshot(&self) -> Result<()>;

    /// Record an index creation operation to WAL for persistence
    ///
    /// This should be called by the executor after creating an index to ensure
    /// the index is recreated on recovery.
    fn record_create_index(
        &self,
        table_name: &str,
        index_name: &str,
        column_names: &[String],
        is_unique: bool,
        index_type: crate::core::IndexType,
    ) {
        // Default implementation does nothing (for in-memory engines)
        let _ = (table_name, index_name, column_names, is_unique, index_type);
    }

    /// Record an index drop operation to WAL for persistence
    fn record_drop_index(&self, table_name: &str, index_name: &str) {
        // Default implementation does nothing
        let _ = (table_name, index_name);
    }

    /// Record ALTER TABLE ADD COLUMN operation to WAL for persistence
    fn record_alter_table_add_column(
        &self,
        table_name: &str,
        column_name: &str,
        data_type: crate::core::DataType,
        nullable: bool,
        default_expr: Option<&str>,
    ) {
        // Default implementation does nothing (for in-memory engines)
        let _ = (table_name, column_name, data_type, nullable, default_expr);
    }

    /// Record ALTER TABLE DROP COLUMN operation to WAL for persistence
    fn record_alter_table_drop_column(&self, table_name: &str, column_name: &str) {
        // Default implementation does nothing
        let _ = (table_name, column_name);
    }

    /// Record ALTER TABLE RENAME COLUMN operation to WAL for persistence
    fn record_alter_table_rename_column(
        &self,
        table_name: &str,
        old_column_name: &str,
        new_column_name: &str,
    ) {
        // Default implementation does nothing
        let _ = (table_name, old_column_name, new_column_name);
    }

    /// Record ALTER TABLE MODIFY COLUMN operation to WAL for persistence
    fn record_alter_table_modify_column(
        &self,
        table_name: &str,
        column_name: &str,
        data_type: crate::core::DataType,
        nullable: bool,
        auto_increment: Option<bool>,
        check_expr: Option<Option<String>>,
    ) {
        // Default implementation does nothing
        let _ = (
            table_name,
            column_name,
            data_type,
            nullable,
            auto_increment,
            check_expr,
        );
    }

    /// Record ALTER TABLE RENAME TO operation to WAL for persistence
    fn record_alter_table_rename(&self, old_table_name: &str, new_table_name: &str) {
        // Default implementation does nothing
        let _ = (old_table_name, new_table_name);
    }

    // --- Sequences ---

    fn sequence_exists(&self, schema_name: &str, sequence_name: &str) -> Result<bool>;

    fn create_sequence(
        &self,
        schema_name: &str,
        sequence_name: &str,
        options: crate::core::SequenceOptions,
    ) -> Result<()>;

    fn alter_sequence(
        &self,
        schema_name: &str,
        sequence_name: &str,
        options: crate::core::SequenceOptions,
    ) -> Result<()>;

    fn drop_sequence(&self, schema_name: &str, sequence_name: &str) -> Result<()>;

    fn nextval(&self, schema_name: &str, sequence_name: &str) -> Result<i64>;

    fn setval(
        &self,
        schema_name: &str,
        sequence_name: &str,
        value: i64,
        is_called: bool,
    ) -> Result<i64>;

    fn list_sequences(&self) -> Result<Vec<(String, String, crate::core::SequenceOptions, i64)>>;

    /// Fetch rows by IDs directly from storage without creating a full transaction.
    ///
    /// This is an optimization for EXISTS subquery evaluation where we only need
    /// to check if rows exist and evaluate predicates. It avoids the ~2-5μs overhead
    /// of creating a new transaction per EXISTS probe.
    ///
    /// The returned rows represent the latest committed state visible to any reader.
    fn fetch_rows_by_ids(&self, table_name: &str, row_ids: &[i64]) -> Result<Vec<(i64, Row)>> {
        // Default implementation: fall back to creating a transaction
        // Concrete implementations can override for better performance
        let _ = (table_name, row_ids);
        Err(crate::core::Error::internal(
            "fetch_rows_by_ids not supported by this engine",
        ))
    }

    /// Get a cached row fetcher for a table.
    ///
    /// This returns a function that can be called repeatedly to fetch rows without
    /// the overhead of looking up the table each time. This is useful for EXISTS
    /// subquery evaluation where we probe the same table many times.
    #[allow(clippy::type_complexity)]
    fn get_row_fetcher(
        &self,
        table_name: &str,
    ) -> Result<Box<dyn Fn(&[i64]) -> Vec<(i64, Row)> + Send + Sync>> {
        // Default implementation: fall back to fetch_rows_by_ids
        let _ = table_name;
        Err(crate::core::Error::internal(
            "get_row_fetcher not supported by this engine",
        ))
    }
}

#[cfg(test)]
mod tests {
    // Engine tests will be implemented when we have concrete implementations
    // For now, just verify the trait compiles correctly

    use super::*;

    // Verify trait is object-safe
    fn _assert_object_safe(_: &dyn Engine) {}
}
