//! Modal layout for centered popup dialogs
//!
//! Implements R-POPUP-DIALOGS: Centered modal dialogs with flexible sizing.

use ratatui::prelude::*;

/// Modal layout areas
///
/// Typical layout:
/// ```text
/// ┌─────────────────────────────────┐
/// │                                 │
/// │   ┌───────────────────────┐    │
/// │   │    Modal Dialog       │    │
/// │   │                       │    │
/// │   │   Content...          │    │
/// │   │                       │    │
/// │   └───────────────────────┘    │
/// │                                 │
/// └─────────────────────────────────┘
/// ```
#[derive(Clone, Copy, Debug)]
pub struct ModalArea {
    /// Modal content area (centered)
    pub modal: Rect,
    /// Full background area
    pub background: Rect,
}

impl ModalArea {
    /// Create modal areas with default sizing (60% width, 70% height)
    #[must_use]
    pub fn new(area: Rect) -> Self {
        Self::with_size(area, 60, 70)
    }

    /// Create modal areas with custom width and height percentages
    ///
    /// # Arguments
    ///
    /// * `area` - Available drawing area
    /// * `width_percent` - Width as percentage of area (10-100)
    /// * `height_percent` - Height as percentage of area (10-100)
    #[must_use]
    pub fn with_size(area: Rect, width_percent: u16, height_percent: u16) -> Self {
        let width_percent = width_percent.clamp(10, 100);
        let height_percent = height_percent.clamp(10, 100);

        let modal_width = (area.width * width_percent / 100).max(10);
        let modal_height = (area.height * height_percent / 100).max(3);

        let x_offset = area.x + (area.width.saturating_sub(modal_width)) / 2;
        let y_offset = area.y + (area.height.saturating_sub(modal_height)) / 2;

        let modal = Rect {
            x: x_offset,
            y: y_offset,
            width: modal_width,
            height: modal_height,
        };

        Self {
            modal,
            background: area,
        }
    }
}

/// Modal layout manager
///
/// Provides a centered modal dialog layout that overlays the background.
/// Often used with a semi-transparent background widget.
///
/// # Example
///
/// ```ignore
/// let modal_layout = ModalLayout::new();
/// let areas = modal_layout.areas(frame.size());
///
/// // Render background (typically opaque widget)
/// frame.render_widget(background, areas.background);
///
/// // Render modal dialog centered on top
/// frame.render_widget(dialog, areas.modal);
/// ```
#[derive(Clone, Debug)]
pub struct ModalLayout {
    /// Modal width as percentage (default 60)
    width_percent: u16,
    /// Modal height as percentage (default 70)
    height_percent: u16,
}

impl ModalLayout {
    /// Create a new modal layout with default sizing
    #[must_use]
    pub const fn new() -> Self {
        Self {
            width_percent: 60,
            height_percent: 70,
        }
    }

    /// Set custom width percentage (10-100)
    #[must_use]
    pub fn width(mut self, percent: u16) -> Self {
        self.width_percent = percent.clamp(10, 100);
        self
    }

    /// Set custom height percentage (10-100)
    #[must_use]
    pub fn height(mut self, percent: u16) -> Self {
        self.height_percent = percent.clamp(10, 100);
        self
    }

    /// Set both width and height
    #[must_use]
    pub fn size(self, width: u16, height: u16) -> Self {
        self.width(width).height(height)
    }

    /// Get width percentage
    #[must_use]
    pub const fn width_percent(&self) -> u16 {
        self.width_percent
    }

    /// Get height percentage
    #[must_use]
    pub const fn height_percent(&self) -> u16 {
        self.height_percent
    }

    /// Calculate areas for the given space
    #[must_use]
    pub fn areas(&self, area: Rect) -> ModalArea {
        ModalArea::with_size(area, self.width_percent, self.height_percent)
    }
}

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

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

    #[test]
    fn test_modal_area_new() {
        let area = Rect {
            x: 0,
            y: 0,
            width: 100,
            height: 50,
        };

        let modal_area = ModalArea::new(area);

        // Modal should be ~60% width, ~70% height
        assert!(modal_area.modal.width >= 50);
        assert!(modal_area.modal.width <= 70);
        assert!(modal_area.modal.height >= 30);
        assert!(modal_area.modal.height <= 40);
    }

    #[test]
    fn test_modal_area_custom_size() {
        let area = Rect {
            x: 0,
            y: 0,
            width: 100,
            height: 50,
        };

        let modal_area = ModalArea::with_size(area, 50, 50);

        // Modal should be ~50% width and height
        assert!(modal_area.modal.width >= 40);
        assert!(modal_area.modal.width <= 60);
        assert!(modal_area.modal.height >= 20);
        assert!(modal_area.modal.height <= 30);
    }

    #[test]
    fn test_modal_area_centered() {
        let area = Rect {
            x: 0,
            y: 0,
            width: 100,
            height: 50,
        };

        let modal_area = ModalArea::new(area);

        // Modal should be roughly centered
        let center_x = area.width / 2;
        let modal_center_x = modal_area.modal.x + modal_area.modal.width / 2;
        assert!((i32::from(modal_center_x) - i32::from(center_x)).abs() <= 5);

        let center_y = area.height / 2;
        let modal_center_y = modal_area.modal.y + modal_area.modal.height / 2;
        assert!((i32::from(modal_center_y) - i32::from(center_y)).abs() <= 5);
    }

    #[test]
    fn test_modal_area_small_terminal() {
        let area = Rect {
            x: 0,
            y: 0,
            width: 20,
            height: 10,
        };

        let modal_area = ModalArea::new(area);

        // Modal should still be readable on small terminal
        assert!(modal_area.modal.width >= 10);
        assert!(modal_area.modal.height >= 3);
    }

    #[test]
    fn test_modal_layout_new() {
        let layout = ModalLayout::new();
        assert_eq!(layout.width_percent(), 60);
        assert_eq!(layout.height_percent(), 70);
    }

    #[test]
    fn test_modal_layout_width() {
        let layout = ModalLayout::new().width(50);
        assert_eq!(layout.width_percent(), 50);
    }

    #[test]
    fn test_modal_layout_height() {
        let layout = ModalLayout::new().height(60);
        assert_eq!(layout.height_percent(), 60);
    }

    #[test]
    fn test_modal_layout_size() {
        let layout = ModalLayout::new().size(50, 60);
        assert_eq!(layout.width_percent(), 50);
        assert_eq!(layout.height_percent(), 60);
    }

    #[test]
    fn test_modal_layout_width_clamp_min() {
        let layout = ModalLayout::new().width(5); // Below min
        assert_eq!(layout.width_percent(), 10); // Clamped to min
    }

    #[test]
    fn test_modal_layout_width_clamp_max() {
        let layout = ModalLayout::new().width(150); // Above max
        assert_eq!(layout.width_percent(), 100); // Clamped to max
    }

    #[test]
    fn test_modal_layout_height_clamp_min() {
        let layout = ModalLayout::new().height(5); // Below min
        assert_eq!(layout.height_percent(), 10); // Clamped to min
    }

    #[test]
    fn test_modal_layout_height_clamp_max() {
        let layout = ModalLayout::new().height(150); // Above max
        assert_eq!(layout.height_percent(), 100); // Clamped to max
    }

    #[test]
    fn test_modal_layout_default() {
        let layout = ModalLayout::default();
        assert_eq!(layout.width_percent(), 60);
        assert_eq!(layout.height_percent(), 70);
    }

    #[test]
    fn test_modal_layout_clone() {
        let layout1 = ModalLayout::new().size(50, 60);
        let layout2 = layout1.clone();
        assert_eq!(layout1.width_percent(), layout2.width_percent());
        assert_eq!(layout1.height_percent(), layout2.height_percent());
    }

    #[test]
    fn test_modal_layout_areas_method() {
        let layout = ModalLayout::new();
        let area = Rect {
            x: 0,
            y: 0,
            width: 100,
            height: 50,
        };

        let modal_area = layout.areas(area);
        assert!(modal_area.modal.width > 0);
        assert!(modal_area.modal.height > 0);
    }

    #[test]
    fn test_modal_area_within_background() {
        let background = Rect {
            x: 0,
            y: 0,
            width: 100,
            height: 50,
        };

        let modal_area = ModalArea::new(background);

        // Modal should be within background bounds
        assert!(modal_area.modal.x >= background.x);
        assert!(modal_area.modal.y >= background.y);
        assert!(modal_area.modal.x + modal_area.modal.width <= background.x + background.width);
        assert!(modal_area.modal.y + modal_area.modal.height <= background.y + background.height);
    }
}
