use std::process::Command;

use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Point, Rectangle, Size};

use crate::drawing::{ForgeElement, RenderTexture, Texture};
use crate::output::Canvas;
use crate::text::TextRenderer;

const OVERLAY_BG: [u8; 4] = [0, 0, 0, 180];
const DIALOG_BG: [u8; 4] = [30, 30, 30, 245];
const BUTTON_BG: [u8; 4] = [50, 50, 50, 255];
const BUTTON_DANGER_BG: [u8; 4] = [160, 50, 50, 255];
const BUTTON_WARN_BG: [u8; 4] = [160, 120, 40, 255];
const BUTTON_LOCK_BG: [u8; 4] = [50, 90, 160, 255];
const BUTTON_LOGOUT_BG: [u8; 4] = [80, 80, 80, 255];
const BUTTON_SAFE_BG: [u8; 4] = [60, 60, 60, 255];
const TEXT_COLOR: [u8; 4] = [255, 255, 255, 255];
const LABEL_COLOR: [u8; 4] = [200, 200, 200, 255];

const DIALOG_WIDTH: i32 = 300;
const BUTTON_HEIGHT: i32 = 48;
const BUTTON_GAP: i32 = 8;
const PADDING: i32 = 20;
const TITLE_BOTTOM_MARGIN: i32 = 14;

/// Number of action buttons (Lock, Suspend, Logout, Reboot, Power Off)
const NUM_ACTIONS: usize = 5;

pub struct PowerMenu {
    pub visible: bool,
    dirty: bool,
    last_scale: f64,

    overlay_bg: RenderTexture,
    dialog_bg: RenderTexture,
    lock_bg: RenderTexture,
    suspend_bg: RenderTexture,
    logout_bg: RenderTexture,
    reboot_bg: RenderTexture,
    poweroff_bg: RenderTexture,
    cancel_bg: RenderTexture,
    title_texture: Option<RenderTexture>,
    lock_label: Option<RenderTexture>,
    suspend_label: Option<RenderTexture>,
    logout_label: Option<RenderTexture>,
    reboot_label: Option<RenderTexture>,
    poweroff_label: Option<RenderTexture>,
    cancel_label: Option<RenderTexture>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PowerAction {
    Lock,
    Suspend,
    Logout,
    Reboot,
    PowerOff,
    Cancel,
}

impl PowerMenu {
    pub fn new() -> Self {
        Self {
            visible: false,
            dirty: true,
            last_scale: 0.0,
            overlay_bg: RenderTexture::new(Texture::from_color(OVERLAY_BG, false)),
            dialog_bg: RenderTexture::new(Texture::from_color(DIALOG_BG, true)),
            lock_bg: RenderTexture::new(Texture::from_color(BUTTON_LOCK_BG, true)),
            suspend_bg: RenderTexture::new(Texture::from_color(BUTTON_BG, true)),
            logout_bg: RenderTexture::new(Texture::from_color(BUTTON_LOGOUT_BG, true)),
            reboot_bg: RenderTexture::new(Texture::from_color(BUTTON_WARN_BG, true)),
            poweroff_bg: RenderTexture::new(Texture::from_color(BUTTON_DANGER_BG, true)),
            cancel_bg: RenderTexture::new(Texture::from_color(BUTTON_SAFE_BG, true)),
            title_texture: None,
            lock_label: None,
            suspend_label: None,
            logout_label: None,
            reboot_label: None,
            poweroff_label: None,
            cancel_label: None,
        }
    }

    pub fn show(&mut self) {
        self.visible = true;
        self.dirty = true;
    }

    pub fn dismiss(&mut self) {
        self.visible = false;
        self.dirty = true;
    }

    /// Show power menu pre-focused on logout action.
    pub fn show_logout(&mut self) {
        self.show();
    }

    /// Show power menu pre-focused on shutdown action.
    pub fn show_shutdown(&mut self) {
        self.show();
    }

    /// Show power menu pre-focused on reboot action.
    pub fn show_reboot(&mut self) {
        self.show();
    }

    /// Compute dialog height: title + action buttons + cancel + padding.
    fn dialog_height(&self) -> i32 {
        PADDING + 24 + TITLE_BOTTOM_MARGIN // title area
            + (NUM_ACTIONS as i32) * BUTTON_HEIGHT + (NUM_ACTIONS as i32 - 1) * BUTTON_GAP
            + BUTTON_GAP * 2 // extra gap before cancel
            + BUTTON_HEIGHT // cancel button
            + PADDING // bottom padding
    }

    /// Compute button rectangles for hit testing.
    /// Order: Lock, Suspend, Logout, Reboot, Power Off, Cancel
    fn button_rects(&self, canvas: &Canvas) -> [Rectangle<i32, Logical>; NUM_ACTIONS + 1] {
        let size = canvas.size();
        let dw = if canvas.is_mobile() { size.w - 32 } else { DIALOG_WIDTH };
        let dx = (size.w - dw) / 2;
        let dh = self.dialog_height();
        let dy = (size.h - dh) / 2;

        let btn_w = dw - 2 * PADDING;
        let btn_x = dx + PADDING;
        let mut btn_y = dy + PADDING + 24 + TITLE_BOTTOM_MARGIN;

        let mut rects = [Rectangle::default(); NUM_ACTIONS + 1];

        for i in 0..NUM_ACTIONS {
            rects[i] = Rectangle::new(Point::from((btn_x, btn_y)), Size::from((btn_w, BUTTON_HEIGHT)));
            btn_y += BUTTON_HEIGHT + BUTTON_GAP;
        }

        // Cancel button (extra gap above)
        btn_y += BUTTON_GAP;
        rects[NUM_ACTIONS] = Rectangle::new(Point::from((btn_x, btn_y)), Size::from((btn_w, BUTTON_HEIGHT)));

        rects
    }

    pub fn handle_tap(&self, canvas: &Canvas, point: Point<f64, Logical>) -> Option<PowerAction> {
        if !self.visible {
            return None;
        }

        let rects = self.button_rects(canvas);
        let actions = [
            PowerAction::Lock,
            PowerAction::Suspend,
            PowerAction::Logout,
            PowerAction::Reboot,
            PowerAction::PowerOff,
            PowerAction::Cancel,
        ];

        for (rect, action) in rects.iter().zip(actions.iter()) {
            if rect.to_f64().contains(point) {
                return Some(*action);
            }
        }

        // Tap outside dialog dismisses
        Some(PowerAction::Cancel)
    }

    pub fn execute(action: PowerAction) {
        // Call logind directly with interactive=false so polkit uses the
        // non-interactive policy (which is "yes" for these actions from an
        // active session). `systemctl poweroff` uses interactive=true and
        // fails silently when no polkit auth agent answers the prompt.
        let method = match action {
            PowerAction::PowerOff => "PowerOff",
            PowerAction::Reboot   => "Reboot",
            PowerAction::Suspend  => "Suspend",
            PowerAction::Lock | PowerAction::Logout | PowerAction::Cancel => return,
        };
        let _ = Command::new("busctl")
            .args([
                "call", "--system",
                "org.freedesktop.login1",
                "/org/freedesktop/login1",
                "org.freedesktop.login1.Manager",
                method,
                "b", "false",
            ])
            .spawn();
    }

    fn update_textures(
        &mut self,
        renderer: &mut GlesRenderer,
        canvas: &Canvas,
        text_renderer: &mut TextRenderer,
    ) {
        let scale = canvas.scale();
        if !self.dirty && (self.last_scale - scale).abs() < 0.01 {
            return;
        }
        self.dirty = false;
        self.last_scale = scale;

        let title_font = (18.0 * scale as f32).round();
        let btn_font = (15.0 * scale as f32).round();

        // Title
        let (buf, w, h) = text_renderer.render_line("Power", title_font, TEXT_COLOR);
        if w > 0 && h > 0 {
            self.title_texture = Some(RenderTexture::new(
                Texture::from_buffer(renderer, scale, &buf, w as i32, h as i32, false),
            ));
        }

        // Button labels
        for (label_text, target) in [
            ("Lock Screen", &mut self.lock_label),
            ("Suspend", &mut self.suspend_label),
            ("Log Out", &mut self.logout_label),
            ("Restart", &mut self.reboot_label),
            ("Power Off", &mut self.poweroff_label),
            ("Cancel", &mut self.cancel_label),
        ] {
            let color = if label_text == "Cancel" { LABEL_COLOR } else { TEXT_COLOR };
            let (buf, w, h) = text_renderer.render_line(label_text, btn_font, color);
            if w > 0 && h > 0 {
                *target = Some(RenderTexture::new(
                    Texture::from_buffer(renderer, scale, &buf, w as i32, h as i32, false),
                ));
            }
        }
    }

    pub fn textures(
        &mut self,
        textures: &mut Vec<ForgeElement>,
        canvas: &Canvas,
        renderer: &mut GlesRenderer,
        text_renderer: &mut TextRenderer,
    ) {
        if !self.visible {
            return;
        }

        self.update_textures(renderer, canvas, text_renderer);

        let scale = canvas.scale();
        let size = canvas.size();

        // Full-screen overlay
        let overlay_rect: Rectangle<i32, Logical> = Rectangle::new(Point::from((0, 0)), size);
        let physical_overlay = overlay_rect.to_physical_precise_round(scale);
        ForgeElement::add_element(
            textures,
            self.overlay_bg.clone(),
            physical_overlay.loc,
            physical_overlay,
            None,
            scale,
        );

        // Dialog box
        let dw = if canvas.is_mobile() { size.w - 32 } else { DIALOG_WIDTH };
        let dh = self.dialog_height();
        let dx = (size.w - dw) / 2;
        let dy = (size.h - dh) / 2;

        let dialog_rect: Rectangle<i32, Logical> =
            Rectangle::new(Point::from((dx, dy)), Size::from((dw, dh)));
        let physical_dialog = dialog_rect.to_physical_precise_round(scale);
        ForgeElement::add_element(
            textures,
            self.dialog_bg.clone(),
            physical_dialog.loc,
            physical_dialog,
            None,
            scale,
        );

        // Title
        if let Some(title) = &self.title_texture {
            let ts = title.size();
            let tx = dx + (dw - ts.w) / 2;
            let ty = dy + PADDING;
            let rect: Rectangle<i32, Logical> = Rectangle::new(Point::from((tx, ty)), ts);
            let physical = rect.to_physical_precise_round(scale);
            ForgeElement::add_element(textures, title.clone(), physical.loc, physical, None, scale);
        }

        // Buttons
        let rects = self.button_rects(canvas);
        let bgs = [
            &self.lock_bg,
            &self.suspend_bg,
            &self.logout_bg,
            &self.reboot_bg,
            &self.poweroff_bg,
            &self.cancel_bg,
        ];
        let labels = [
            &self.lock_label,
            &self.suspend_label,
            &self.logout_label,
            &self.reboot_label,
            &self.poweroff_label,
            &self.cancel_label,
        ];

        for (i, (rect, bg)) in rects.iter().zip(bgs.iter()).enumerate() {
            let physical_btn = rect.to_physical_precise_round(scale);
            ForgeElement::add_element(
                textures,
                (*bg).clone(),
                physical_btn.loc,
                physical_btn,
                None,
                scale,
            );

            if let Some(label) = labels[i] {
                let ls = label.size();
                let lx = rect.loc.x + (rect.size.w - ls.w) / 2;
                let ly = rect.loc.y + (BUTTON_HEIGHT - ls.h) / 2;
                let label_rect: Rectangle<i32, Logical> =
                    Rectangle::new(Point::from((lx, ly)), ls);
                let physical = label_rect.to_physical_precise_round(scale);
                ForgeElement::add_element(
                    textures,
                    label.clone(),
                    physical.loc,
                    physical,
                    None,
                    scale,
                );
            }
        }
    }
}

// The org.gnome.SessionManager.EndSessionDialog interface is implemented by
// session_manager_dbus.rs, which owns the org.gnome.SessionManager name and
// all of its sub-interfaces.
