use std::process::Command;
use std::time::{Duration, Instant};

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

use crate::canvas::{Color, SoftCanvas};
use crate::drawing::{ForgeElement, RenderTexture, Texture};
use crate::output::Canvas;

const OSD_WIDTH: i32 = 260;
const OSD_HEIGHT: i32 = 48;
const OSD_BOTTOM_MARGIN: i32 = 80;
const BAR_HEIGHT: f32 = 6.0;
const BAR_MARGIN: f32 = 12.0;
const OSD_DURATION: Duration = Duration::from_millis(1500);
const OSD_RADIUS: f32 = 12.0;

const BG_COLOR: Color      = Color::rgba8(20, 20, 20, 200);
const FILL_COLOR: Color    = Color::rgba8(100, 160, 255, 255);
const MUTED_COLOR: Color   = Color::rgba8(180, 60, 60, 255);
const TEXT_COLOR: Color     = Color::rgba8(255, 255, 255, 255);
const TRACK_COLOR: Color   = Color::rgba8(60, 60, 60, 255);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OsdKind {
    Volume,
    Brightness,
}

pub struct Osd {
    pub visible: bool,
    kind: OsdKind,
    percent: u8,
    muted: bool,
    show_time: Instant,
    dirty: bool,
    last_scale: f64,
    texture: Option<RenderTexture>,
    canvas: Option<SoftCanvas>,
}

impl Osd {
    pub fn new() -> Self {
        Self {
            visible: false,
            kind: OsdKind::Volume,
            percent: 0,
            muted: false,
            show_time: Instant::now(),
            dirty: true,
            last_scale: 0.0,
            texture: None,
            canvas: None,
        }
    }

    pub fn show(&mut self, kind: OsdKind, percent: u8, muted: bool) {
        self.kind = kind;
        self.percent = percent.min(100);
        self.muted = muted;
        self.visible = true;
        self.show_time = Instant::now();
        self.dirty = true;
    }

    pub fn maybe_expire(&mut self) -> bool {
        if self.visible && self.show_time.elapsed() >= OSD_DURATION {
            self.visible = false;
            self.dirty = true;
            return true;
        }
        false
    }

    fn render(&mut self, renderer: &mut GlesRenderer, osd_w: i32, scale: f64) {
        if !self.dirty && (self.last_scale - scale).abs() < 0.01 {
            return;
        }
        self.dirty = false;
        self.last_scale = scale;

        let pw = (osd_w as f64 * scale).round() as u32;
        let ph = (OSD_HEIGHT as f64 * scale).round() as u32;

        let canvas = self.canvas.get_or_insert_with(|| SoftCanvas::new(pw, ph));
        if canvas.width() != pw || canvas.height() != ph {
            *canvas = SoftCanvas::new(pw, ph);
        }
        canvas.clear();

        let w = pw as f32;
        let h = ph as f32;
        let m = BAR_MARGIN * scale as f32;

        // Background with rounded corners
        canvas.fill_rect(0.0, 0.0, w, h, BG_COLOR, OSD_RADIUS * scale as f32);

        // Label text
        let font_size = (13.0 * scale as f32).round();
        let label = match (self.kind, self.muted) {
            (OsdKind::Volume, true) => format!("Vol  {}% (muted)", self.percent),
            (OsdKind::Volume, false) => format!("Vol  {}%", self.percent),
            (OsdKind::Brightness, _) => format!("Bri  {}%", self.percent),
        };
        let text_y = (h - BAR_HEIGHT * scale as f32 - m - font_size) / 2.0 + font_size;
        canvas.draw_text(&label, m, text_y, font_size, TEXT_COLOR);

        // Progress bar track
        let bar_w = w - 2.0 * m;
        let bar_y = h - BAR_HEIGHT * scale as f32 - m;
        let bar_h = BAR_HEIGHT * scale as f32;
        canvas.fill_rect(m, bar_y, bar_w, bar_h, TRACK_COLOR, bar_h / 2.0);

        // Progress bar fill
        let fill_w = bar_w * self.percent as f32 / 100.0;
        if fill_w > 0.0 {
            let fill_color = if self.muted { MUTED_COLOR } else { FILL_COLOR };
            canvas.fill_rect(m, bar_y, fill_w, bar_h, fill_color, bar_h / 2.0);
        }

        // Upload as single texture
        let tex = Texture::from_buffer(
            renderer, scale,
            canvas.data(), pw as i32, ph as i32, false,
        );
        self.texture = Some(RenderTexture::new(tex));
    }

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

        let scale = canvas.scale();
        let size = canvas.size();
        let osd_w = if canvas.is_mobile() { size.w - 40 } else { OSD_WIDTH };

        self.render(renderer, osd_w, scale);

        if let Some(tex) = &self.texture {
            let ox = (size.w - osd_w) / 2;
            let oy = size.h - OSD_HEIGHT - OSD_BOTTOM_MARGIN;
            let rect: Rectangle<i32, Logical> =
                Rectangle::new(Point::from((ox, oy)), Size::from((osd_w, OSD_HEIGHT)));
            let physical = rect.to_physical_precise_round(scale);
            ForgeElement::add_element(textures, tex.clone(), physical.loc, physical, None, scale);
        }
    }
}

// ── Volume/brightness helpers ───────────────────────────────────────────

pub fn adjust_volume(up: bool) -> (u8, bool) {
    let step = if up { "+5%" } else { "-5%" };
    let _ = Command::new("pactl")
        .args(["set-sink-volume", "@DEFAULT_SINK@", step])
        .output();
    read_volume()
}

pub fn toggle_mute() -> (u8, bool) {
    let _ = Command::new("pactl")
        .args(["set-sink-mute", "@DEFAULT_SINK@", "toggle"])
        .output();
    read_volume()
}

fn read_volume() -> (u8, bool) {
    let vol_output = Command::new("pactl")
        .args(["get-sink-volume", "@DEFAULT_SINK@"])
        .output();

    let percent = vol_output
        .ok()
        .and_then(|o| {
            let s = String::from_utf8_lossy(&o.stdout);
            s.split('%')
                .next()
                .and_then(|p| p.split_whitespace().last())
                .and_then(|v| v.parse::<u8>().ok())
        })
        .unwrap_or(50);

    let muted = Command::new("pactl")
        .args(["get-sink-mute", "@DEFAULT_SINK@"])
        .output()
        .map(|o| String::from_utf8_lossy(&o.stdout).contains("yes"))
        .unwrap_or(false);

    (percent, muted)
}

pub fn adjust_brightness(up: bool) -> u8 {
    crate::backlight::adjust(up)
}
