use std::sync::Arc;
use teloxide::types::{InlineKeyboardMarkup, InlineKeyboardButton};

/// Configuration for pagination behavior
#[derive(Clone)]
pub struct PaginationConfig {
    pub items_per_page: usize,
    pub items_per_row: usize,
    pub show_page_info: bool,
    pub prev_button_text: String,
    pub next_button_text: String,
    pub page_info_template: String, // "{current}/{total}"
}

impl Default for PaginationConfig {
    fn default() -> Self {
        Self {
            items_per_page: 10,
            items_per_row: 2,
            show_page_info: true,
            prev_button_text: "⬅️".to_string(),
            next_button_text: "➡️".to_string(),
            page_info_template: "{current}/{total}".to_string(),
        }
    }
}

/// Pagination result containing the processed data
#[derive(Debug, Clone)]
pub struct PaginationResult<T> {
    pub items: Vec<T>,
    pub current_page: usize,
    pub total_pages: usize,
    pub total_items: usize,
    pub start_index: usize,
    pub end_index: usize,
    pub has_previous: bool,
    pub has_next: bool,
}

/// Generic pagination utility
pub struct Paginator<T> {
    items: Vec<T>,
    config: PaginationConfig,
}

impl<T: Clone> Paginator<T> {
    /// Create a new paginator with items and default configuration
    pub fn new(items: Vec<T>) -> Self {
        Self {
            items,
            config: PaginationConfig::default(),
        }
    }

    /// Create a new paginator with custom configuration
    pub fn with_config(items: Vec<T>, config: PaginationConfig) -> Self {
        Self { items, config }
    }

    /// Get paginated result for a specific page
    pub fn paginate(&self, page: usize) -> PaginationResult<T> {
        let total_items = self.items.len();
        let total_pages = if total_items == 0 {
            1
        } else {
            total_items.div_ceil(self.config.items_per_page)
        };
        
        let current_page = page.min(total_pages.saturating_sub(1));
        let start_index = current_page * self.config.items_per_page;
        let end_index = std::cmp::min(start_index + self.config.items_per_page, total_items);
        
        let page_items = if start_index < total_items {
            self.items[start_index..end_index].to_vec()
        } else {
            Vec::new()
        };

        PaginationResult {
            items: page_items,
            current_page,
            total_pages,
            total_items,
            start_index,
            end_index,
            has_previous: current_page > 0,
            has_next: current_page + 1 < total_pages,
        }
    }

    /// Get all items without pagination
    pub fn all_items(&self) -> &Vec<T> {
        &self.items
    }

    /// Get configuration
    pub fn config(&self) -> &PaginationConfig {
        &self.config
    }
}

/// Builder for creating pagination keyboards
pub struct PaginationKeyboardBuilder<C> {
    result: PaginationResult<C>,
    config: PaginationConfig,
    page_callback: Arc<dyn Fn(usize) -> String + Send + Sync>,
    no_op_callback: String,
}

impl<C> PaginationKeyboardBuilder<C> {
    /// Create a new keyboard builder
    pub fn new(
        result: PaginationResult<C>,
        config: PaginationConfig,
        page_callback: Arc<dyn Fn(usize) -> String + Send + Sync>,
        no_op_callback: String,
    ) -> Self {
        Self {
            result,
            config,
            page_callback,
            no_op_callback,
        }
    }

    /// Build the pagination keyboard with custom item renderer
    pub fn build<F>(&self, item_renderer: F) -> InlineKeyboardMarkup
    where
        F: Fn(usize, &C) -> InlineKeyboardButton,
    {
        let mut keyboard = InlineKeyboardMarkup::default();

        if !self.result.items.is_empty() {
            let mut item_index = 0;
            for row_items in self.result.items.chunks(self.config.items_per_row) {
                let mut row = Vec::new();
                for item in row_items.iter() {
                    let global_index = self.result.start_index + item_index;
                    row.push(item_renderer(global_index, item));
                    item_index += 1;
                }
                keyboard = keyboard.append_row(row);
            }
        }

        self.add_pagination_controls(&mut keyboard);

        keyboard
    }

    /// Build pagination keyboard with simple text buttons
    pub fn build_simple<F>(&self, text_renderer: F) -> InlineKeyboardMarkup
    where
        F: Fn(usize, &C) -> (String, String), // (button_text, callback_data)
    {
        self.build(|index, item| {
            let (text, callback) = text_renderer(index, item);
            InlineKeyboardButton::callback(text, callback)
        })
    }

    /// Add pagination navigation controls to keyboard
    pub fn add_pagination_controls(&self, keyboard: &mut InlineKeyboardMarkup) {
        if self.result.total_pages > 1 {
            let mut pagination_row = Vec::new();

            if self.result.has_previous {
                pagination_row.push(InlineKeyboardButton::callback(
                    self.config.prev_button_text.clone(),
                    (self.page_callback)(self.result.current_page - 1),
                ));
            }

            if self.config.show_page_info {
                let page_info = self.config.page_info_template
                    .replace("{current}", &(self.result.current_page + 1).to_string())
                    .replace("{total}", &self.result.total_pages.to_string());
                
                pagination_row.push(InlineKeyboardButton::callback(
                    page_info,
                    self.no_op_callback.clone(),
                ));
            }

            if self.result.has_next {
                pagination_row.push(InlineKeyboardButton::callback(
                    self.config.next_button_text.clone(),
                    (self.page_callback)(self.result.current_page + 1),
                ));
            }

            if !pagination_row.is_empty() {
                *keyboard = keyboard.clone().append_row(pagination_row);
            }
        }
    }
}

/// Helper trait for items that can be searched/filtered
pub trait Searchable {
    fn matches_query(&self, query: &str) -> bool;
}

/// Paginator with search functionality
pub struct SearchablePaginator<T> {
    all_items: Vec<T>,
    filtered_items: Vec<T>,
    current_query: String,
    config: PaginationConfig,
}

impl<T: Clone + Searchable> SearchablePaginator<T> {
    /// Create a new searchable paginator
    pub fn new(items: Vec<T>) -> Self {
        Self {
            filtered_items: items.clone(),
            all_items: items,
            current_query: String::new(),
            config: PaginationConfig::default(),
        }
    }

    /// Create with custom configuration
    pub fn with_config(items: Vec<T>, config: PaginationConfig) -> Self {
        Self {
            filtered_items: items.clone(),
            all_items: items,
            current_query: String::new(),
            config,
        }
    }

    /// Apply search filter
    pub fn search(&mut self, query: &str) -> &mut Self {
        self.current_query = query.to_string();
        
        if query.is_empty() {
            self.filtered_items = self.all_items.clone();
        } else {
            let query_lower = query.to_lowercase();
            self.filtered_items = self.all_items
                .iter()
                .filter(|item| item.matches_query(&query_lower))
                .cloned()
                .collect();
        }
        
        self
    }

    /// Clear search and show all items
    pub fn clear_search(&mut self) -> &mut Self {
        self.search("")
    }

    /// Get paginated result
    pub fn paginate(&self, page: usize) -> PaginationResult<T> {
        Paginator::with_config(self.filtered_items.clone(), self.config.clone())
            .paginate(page)
    }

    /// Get current search query
    pub fn current_query(&self) -> &str {
        &self.current_query
    }

    /// Get total filtered items count
    pub fn filtered_count(&self) -> usize {
        self.filtered_items.len()
    }

    /// Get all items count
    pub fn total_count(&self) -> usize {
        self.all_items.len()
    }
}

/// Convenience methods for common pagination patterns
impl PaginationConfig {
    /// Configuration for telegram inline keyboards with emojis
    pub fn telegram_default() -> Self {
        Self {
            items_per_page: 10,
            items_per_row: 2,
            show_page_info: true,
            prev_button_text: "⬅️".to_string(),
            next_button_text: "➡️".to_string(),
            page_info_template: "{current}/{total}".to_string(),
        }
    }

    /// Configuration for large lists
    pub fn large_list() -> Self {
        Self {
            items_per_page: 20,
            items_per_row: 1,
            show_page_info: true,
            prev_button_text: "⬅️ Предыдущая".to_string(),
            next_button_text: "Следующая ➡️".to_string(),
            page_info_template: "Страница {current} из {total}".to_string(),
        }
    }

    /// Configuration for compact lists
    pub fn compact() -> Self {
        Self {
            items_per_page: 18,
            items_per_row: 2,
            show_page_info: true,
            prev_button_text: "⬅️".to_string(),
            next_button_text: "➡️".to_string(),
            page_info_template: "{current}/{total}".to_string(),
        }
    }
}

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

    #[derive(Clone, Debug, PartialEq)]
    struct TestItem {
        id: u32,
        name: String,
    }

    impl Searchable for TestItem {
        fn matches_query(&self, query: &str) -> bool {
            self.name.to_lowercase().contains(query)
        }
    }

    #[test]
    fn test_basic_pagination() {
        let items = vec![
            TestItem { id: 1, name: "Item 1".to_string() },
            TestItem { id: 2, name: "Item 2".to_string() },
            TestItem { id: 3, name: "Item 3".to_string() },
        ];

        let paginator = Paginator::new(items);
        let result = paginator.paginate(0);

        assert_eq!(result.current_page, 0);
        assert_eq!(result.total_pages, 1);
        assert_eq!(result.items.len(), 3);
        assert!(!result.has_previous);
        assert!(!result.has_next);
    }

    #[test]
    fn test_pagination_with_multiple_pages() {
        let items: Vec<TestItem> = (1..=25)
            .map(|i| TestItem { id: i, name: format!("Item {}", i) })
            .collect();

        let config = PaginationConfig {
            items_per_page: 10,
            ..Default::default()
        };

        let paginator = Paginator::with_config(items, config);
        
        let result = paginator.paginate(0);
        assert_eq!(result.current_page, 0);
        assert_eq!(result.total_pages, 3);
        assert_eq!(result.items.len(), 10);
        assert!(!result.has_previous);
        assert!(result.has_next);

        let result = paginator.paginate(1);
        assert_eq!(result.current_page, 1);
        assert_eq!(result.items.len(), 10);
        assert!(result.has_previous);
        assert!(result.has_next);

        let result = paginator.paginate(2);
        assert_eq!(result.current_page, 2);
        assert_eq!(result.items.len(), 5);
        assert!(result.has_previous);
        assert!(!result.has_next);
    }

    #[test]
    fn test_searchable_pagination() {
        let items = vec![
            TestItem { id: 1, name: "Apple".to_string() },
            TestItem { id: 2, name: "Banana".to_string() },
            TestItem { id: 3, name: "Cherry".to_string() },
            TestItem { id: 4, name: "Date".to_string() },
        ];

        let mut paginator = SearchablePaginator::new(items);
        
        paginator.search("a");
        let result = paginator.paginate(0);
        
        assert_eq!(result.items.len(), 3); // Apple, Banana, Date
        assert_eq!(paginator.filtered_count(), 3);
        assert_eq!(paginator.total_count(), 4);

        paginator.clear_search();
        let result = paginator.paginate(0);
        assert_eq!(result.items.len(), 4);
    }
} 