//! Renders the combined repository attention count as a taskbar badge and applies it as the
//! Windows taskbar overlay. Windows has no native numeric taskbar badge (unlike
//! macOS/Linux, which support `Window::set_badge_count`), so the numbers have to be drawn
//! into a plain RGBA icon ourselves and set via `Window::set_overlay_icon`.

use tauri::Manager;
use tauri::image::Image;

const GLYPH_WIDTH: usize = 3;
const GLYPH_HEIGHT: usize = 5;

/// A minimal 3x5 pixel-grid font, just enough to draw digits and "+" legibly at badge size.
fn glyph_rows(ch: char) -> [&'static str; GLYPH_HEIGHT] {
    match ch {
        '0' => ["111", "101", "101", "101", "111"],
        '1' => ["010", "110", "010", "010", "111"],
        '2' => ["111", "001", "111", "100", "111"],
        '3' => ["111", "001", "111", "001", "111"],
        '4' => ["101", "101", "111", "001", "001"],
        '5' => ["111", "100", "111", "001", "111"],
        '6' => ["111", "100", "111", "101", "111"],
        '7' => ["111", "001", "010", "010", "010"],
        '8' => ["111", "101", "111", "101", "111"],
        '9' => ["111", "101", "111", "001", "111"],
        '+' => ["000", "010", "111", "010", "000"],
        _ => ["000", "000", "000", "000", "000"],
    }
}

const BADGE_FILL: [u8; 4] = [224, 160, 64, 255];
const BADGE_BORDER: [u8; 4] = [176, 118, 40, 255];
const TEXT_FILL: [u8; 4] = [255, 255, 255, 255];
const TEXT_SHADOW: [u8; 4] = [10, 12, 24, 190];
const CIRCLE_BORDER_WIDTH: f32 = 3.0;

/// Caps the displayed text at three characters ("99+") so it always fits legibly.
fn cap_text(count: u32) -> String {
    if count > 99 {
        "99+".to_string()
    } else {
        count.to_string()
    }
}

/// Draws a filled circle (with a slightly darker rim) centered at `(cx, cy)`.
fn draw_circle(
    rgba: &mut [u8],
    size: usize,
    cx: f32,
    cy: f32,
    radius: f32,
    fill: [u8; 4],
    border: [u8; 4],
) {
    for y in 0..size {
        for x in 0..size {
            let dx = x as f32 + 0.5 - cx;
            let dy = y as f32 + 0.5 - cy;
            let dist = (dx * dx + dy * dy).sqrt();
            if dist <= radius {
                let color = if dist >= radius - CIRCLE_BORDER_WIDTH {
                    border
                } else {
                    fill
                };
                let idx = (y * size + x) * 4;
                rgba[idx..idx + 4].copy_from_slice(&color);
            }
        }
    }
}

fn draw_text_layer(
    rgba: &mut [u8],
    size: usize,
    text: &str,
    cx: f32,
    cy: f32,
    scale: usize,
    offset: (isize, isize),
    color: [u8; 4],
) {
    let glyphs: Vec<[&'static str; GLYPH_HEIGHT]> = text.chars().map(glyph_rows).collect();
    let glyph_px_w = GLYPH_WIDTH * scale;
    let glyph_px_h = GLYPH_HEIGHT * scale;
    let gap = scale.max(1);
    let total_w = glyphs.len() * glyph_px_w + gap * glyphs.len().saturating_sub(1);
    let start_x = (cx - total_w as f32 / 2.0).round() as isize + offset.0;
    let start_y = (cy - glyph_px_h as f32 / 2.0).round() as isize + offset.1;

    for (gi, rows) in glyphs.iter().enumerate() {
        let glyph_x = start_x + (gi * (glyph_px_w + gap)) as isize;
        for (gy, row) in rows.iter().enumerate() {
            for (gx, pixel) in row.chars().enumerate() {
                if pixel != '1' {
                    continue;
                }
                for py in 0..scale {
                    for px in 0..scale {
                        let x = glyph_x + (gx * scale + px) as isize;
                        let y = start_y + (gy * scale + py) as isize;
                        if x >= 0 && y >= 0 && (x as usize) < size && (y as usize) < size {
                            let idx = (y as usize * size + x as usize) * 4;
                            rgba[idx..idx + 4].copy_from_slice(&color);
                        }
                    }
                }
            }
        }
    }
}

/// Draws white `text` centered at `(cx, cy)`, scaling each font pixel up by `scale`.
fn draw_text(rgba: &mut [u8], size: usize, text: &str, cx: f32, cy: f32, scale: usize) {
    for offset in [(-1, 0), (1, 0), (0, -1), (0, 1)] {
        draw_text_layer(rgba, size, text, cx, cy, scale, offset, TEXT_SHADOW);
    }
    draw_text_layer(rgba, size, text, cx, cy, scale, (0, 0), TEXT_FILL);
}

fn text_scale(text: &str) -> usize {
    match text.len() {
        0 | 1 => 7,
        2 => 5,
        _ => 4,
    }
}

/// Renders a single large badge with `count`. Returns `None` when there's nothing to show,
/// so the caller can clear the overlay icon.
fn render_badge_icon(count: u32) -> Option<Image<'static>> {
    if count == 0 {
        return None;
    }

    const SIZE: usize = 64;
    const RADIUS: f32 = 29.0;
    let mut rgba = vec![0u8; SIZE * SIZE * 4];
    let center = SIZE as f32 / 2.0;

    let text = cap_text(count);
    draw_circle(
        &mut rgba,
        SIZE,
        center,
        center,
        RADIUS,
        BADGE_FILL,
        BADGE_BORDER,
    );
    draw_text(&mut rgba, SIZE, &text, center, center, text_scale(&text));

    Some(Image::new_owned(rgba, SIZE as u32, SIZE as u32))
}

/// Sets the taskbar badge to `ahead + behind + changes` (0 clears it). Windows-only: Windows
/// has no native numeric badge API, so this draws and applies a small overlay icon instead.
/// No-op on other platforms: non-Windows desktops should use `Window::set_badge_count`
/// for a real native badge instead, which this app doesn't currently wire up.
#[tauri::command]
pub fn set_sync_badge(
    app: tauri::AppHandle,
    ahead: u32,
    behind: u32,
    changes: u32,
) -> Result<(), String> {
    let count = ahead.saturating_add(behind).saturating_add(changes);

    #[cfg(target_os = "windows")]
    {
        let Some(window) = app.get_webview_window("main") else {
            return Ok(());
        };
        let icon = render_badge_icon(count);
        window
            .set_overlay_icon(icon)
            .map_err(|err| format!("Could not set taskbar badge: {err}"))?;
    }

    // Native badge count: works on Linux (via the desktop's launcher API, e.g. Unity's
    // LauncherEntry, also honored by GNOME/KDE) and macOS (dock badge). Matched against the
    // app's `<product-name>.desktop` file on Linux, so it's a silent no-op on launchers that
    // don't implement the protocol.
    #[cfg(any(target_os = "linux", target_os = "macos"))]
    {
        let Some(window) = app.get_webview_window("main") else {
            return Ok(());
        };
        window
            .set_badge_count(if count > 0 { Some(count as i64) } else { None })
            .map_err(|err| format!("Could not set taskbar badge: {err}"))?;
    }

    #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
    {
        let _ = (app, count);
    }

    Ok(())
}
