#![allow(dead_code)] // Phase 0: types defined now, consumed in Phase 1+

//! Flexoki-based theme for the lore TUI.
//!
//! Uses FrankenTUI's `AdaptiveColor::adaptive(light, dark)` for automatic
//! light/dark mode switching. The palette is [Flexoki](https://stephango.com/flexoki)
//! by Steph Ango, designed in Oklab perceptual color space for balanced contrast.

use ftui::{AdaptiveColor, Color, PackedRgba, Style, Theme};

// ---------------------------------------------------------------------------
// Flexoki palette constants
// ---------------------------------------------------------------------------

// Base tones
const PAPER: Color = Color::rgb(0xFF, 0xFC, 0xF0);
const BASE_50: Color = Color::rgb(0xF2, 0xF0, 0xE5);
const BASE_100: Color = Color::rgb(0xE6, 0xE4, 0xD9);
const BASE_200: Color = Color::rgb(0xCE, 0xCD, 0xC3);
const BASE_300: Color = Color::rgb(0xB7, 0xB5, 0xAC);
const BASE_400: Color = Color::rgb(0x9F, 0x9D, 0x96);
const BASE_500: Color = Color::rgb(0x87, 0x85, 0x80);
const BASE_600: Color = Color::rgb(0x6F, 0x6E, 0x69);
const BASE_700: Color = Color::rgb(0x57, 0x56, 0x53);
const BASE_800: Color = Color::rgb(0x40, 0x3E, 0x3C);
const BASE_850: Color = Color::rgb(0x34, 0x33, 0x31);
const BASE_900: Color = Color::rgb(0x28, 0x27, 0x26);
const BLACK: Color = Color::rgb(0x10, 0x0F, 0x0F);

// Accent colors — light-600 (for light mode)
const RED_600: Color = Color::rgb(0xAF, 0x30, 0x29);
const ORANGE_600: Color = Color::rgb(0xBC, 0x52, 0x15);
const YELLOW_600: Color = Color::rgb(0xAD, 0x83, 0x01);
const GREEN_600: Color = Color::rgb(0x66, 0x80, 0x0B);
const CYAN_600: Color = Color::rgb(0x24, 0x83, 0x7B);
const BLUE_600: Color = Color::rgb(0x20, 0x5E, 0xA6);
const PURPLE_600: Color = Color::rgb(0x5E, 0x40, 0x9D);

// Accent colors — dark-400 (for dark mode)
const RED_400: Color = Color::rgb(0xD1, 0x4D, 0x41);
const ORANGE_400: Color = Color::rgb(0xDA, 0x70, 0x2C);
const YELLOW_400: Color = Color::rgb(0xD0, 0xA2, 0x15);
const GREEN_400: Color = Color::rgb(0x87, 0x9A, 0x39);
const CYAN_400: Color = Color::rgb(0x3A, 0xA9, 0x9F);
const BLUE_400: Color = Color::rgb(0x43, 0x85, 0xBE);
const PURPLE_400: Color = Color::rgb(0x8B, 0x7E, 0xC8);
const MAGENTA_400: Color = Color::rgb(0xCE, 0x5D, 0x97);

// Muted fallback as PackedRgba (for Style::fg)
const MUTED_PACKED: PackedRgba = PackedRgba::rgb(0x87, 0x85, 0x80);

// ---------------------------------------------------------------------------
// build_theme
// ---------------------------------------------------------------------------

/// Build the lore TUI theme with Flexoki adaptive colors.
///
/// Each of the 19 semantic slots gets an `AdaptiveColor::adaptive(light, dark)`
/// pair. FrankenTUI detects the terminal background and resolves accordingly.
#[must_use]
pub fn build_theme() -> Theme {
    Theme::builder()
        .primary(AdaptiveColor::adaptive(BLUE_600, BLUE_400))
        .secondary(AdaptiveColor::adaptive(CYAN_600, CYAN_400))
        .accent(AdaptiveColor::adaptive(PURPLE_600, PURPLE_400))
        .background(AdaptiveColor::adaptive(PAPER, BLACK))
        .surface(AdaptiveColor::adaptive(BASE_50, BASE_900))
        .overlay(AdaptiveColor::adaptive(BASE_100, BASE_850))
        .text(AdaptiveColor::adaptive(BASE_700, BASE_200))
        .text_muted(AdaptiveColor::adaptive(BASE_500, BASE_500))
        .text_subtle(AdaptiveColor::adaptive(BASE_400, BASE_600))
        .success(AdaptiveColor::adaptive(GREEN_600, GREEN_400))
        .warning(AdaptiveColor::adaptive(YELLOW_600, YELLOW_400))
        .error(AdaptiveColor::adaptive(RED_600, RED_400))
        .info(AdaptiveColor::adaptive(BLUE_600, BLUE_400))
        .border(AdaptiveColor::adaptive(BASE_300, BASE_700))
        .border_focused(AdaptiveColor::adaptive(BLUE_600, BLUE_400))
        .selection_bg(AdaptiveColor::adaptive(BASE_100, BASE_800))
        .selection_fg(AdaptiveColor::adaptive(BASE_700, BASE_100))
        .scrollbar_track(AdaptiveColor::adaptive(BASE_50, BASE_900))
        .scrollbar_thumb(AdaptiveColor::adaptive(BASE_300, BASE_700))
        .build()
}

// ---------------------------------------------------------------------------
// State colors
// ---------------------------------------------------------------------------

/// Map a GitLab entity state to a display color.
///
/// Returns fixed (non-adaptive) colors — state indicators should be
/// consistent regardless of light/dark mode.
#[must_use]
pub fn state_color(state: &str) -> Color {
    match state {
        "opened" => GREEN_400,
        "closed" => RED_400,
        "merged" => PURPLE_400,
        "locked" => YELLOW_400,
        _ => BASE_500,
    }
}

// ---------------------------------------------------------------------------
// Event type colors
// ---------------------------------------------------------------------------

/// Map a timeline event type to a display color.
#[must_use]
pub fn event_color(event_type: &str) -> Color {
    match event_type {
        "created" => GREEN_400,
        "updated" => BLUE_400,
        "closed" => RED_400,
        "merged" => PURPLE_400,
        "commented" => CYAN_400,
        "labeled" => ORANGE_400,
        "milestoned" => YELLOW_400,
        _ => BASE_500,
    }
}

// ---------------------------------------------------------------------------
// Label styling
// ---------------------------------------------------------------------------

/// Convert a GitLab label hex color (e.g., "#FF0000" or "FF0000") to a Style.
///
/// Falls back to muted text color if the hex string is invalid.
#[must_use]
pub fn label_style(hex_color: &str) -> Style {
    let packed = parse_hex_to_packed(hex_color).unwrap_or(MUTED_PACKED);
    Style::default().fg(packed)
}

/// Parse a hex color string like "#RRGGBB" or "RRGGBB" into a `PackedRgba`.
fn parse_hex_to_packed(s: &str) -> Option<PackedRgba> {
    let hex = s.strip_prefix('#').unwrap_or(s);
    if hex.len() != 6 {
        return None;
    }
    let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
    let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
    let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
    Some(PackedRgba::rgb(r, g, b))
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

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

    #[test]
    fn test_build_theme_compiles() {
        let theme = build_theme();
        // Resolve for dark mode — primary should be Blue-400
        let resolved = theme.resolve(true);
        assert_eq!(resolved.primary, BLUE_400);
    }

    #[test]
    fn test_build_theme_light_mode() {
        let theme = build_theme();
        let resolved = theme.resolve(false);
        assert_eq!(resolved.primary, BLUE_600);
    }

    #[test]
    fn test_build_theme_all_slots_differ_between_modes() {
        let theme = build_theme();
        let dark = theme.resolve(true);
        let light = theme.resolve(false);
        // Background should differ (Paper vs Black)
        assert_ne!(dark.background, light.background);
        // Text should differ
        assert_ne!(dark.text, light.text);
    }

    #[test]
    fn test_state_color_opened_is_green() {
        assert_eq!(state_color("opened"), GREEN_400);
    }

    #[test]
    fn test_state_color_closed_is_red() {
        assert_eq!(state_color("closed"), RED_400);
    }

    #[test]
    fn test_state_color_merged_is_purple() {
        assert_eq!(state_color("merged"), PURPLE_400);
    }

    #[test]
    fn test_state_color_unknown_returns_muted() {
        assert_eq!(state_color("unknown"), BASE_500);
    }

    #[test]
    fn test_event_color_created_is_green() {
        assert_eq!(event_color("created"), GREEN_400);
    }

    #[test]
    fn test_event_color_unknown_returns_muted() {
        assert_eq!(event_color("whatever"), BASE_500);
    }

    #[test]
    fn test_label_style_valid_hex_with_hash() {
        let style = label_style("#FF0000");
        assert_eq!(style.fg, Some(PackedRgba::rgb(0xFF, 0x00, 0x00)));
    }

    #[test]
    fn test_label_style_valid_hex_without_hash() {
        let style = label_style("00FF00");
        assert_eq!(style.fg, Some(PackedRgba::rgb(0x00, 0xFF, 0x00)));
    }

    #[test]
    fn test_label_style_lowercase_hex() {
        let style = label_style("#ff0000");
        assert_eq!(style.fg, Some(PackedRgba::rgb(0xFF, 0x00, 0x00)));
    }

    #[test]
    fn test_label_style_invalid_hex_fallback() {
        let style = label_style("invalid");
        assert_eq!(style.fg, Some(MUTED_PACKED));
    }

    #[test]
    fn test_label_style_empty_fallback() {
        let style = label_style("");
        assert_eq!(style.fg, Some(MUTED_PACKED));
    }

    #[test]
    fn test_parse_hex_short_string() {
        assert!(parse_hex_to_packed("#FFF").is_none());
    }

    #[test]
    fn test_parse_hex_non_hex_chars() {
        assert!(parse_hex_to_packed("#GGHHII").is_none());
    }
}
