/// Typography scale — named text styles with semantic meaning.
///
/// Don't hardcode font sizes. Use the named scale. The theme maps each
/// style to the right size, weight, and line height for the platform.

/// Named text style levels.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TextStyle {
    /// Largest — hero headings, splash screens.
    Display,
    /// Page-level headings.
    Headline,
    /// Section headings, dialog titles.
    Title,
    /// Default readable body copy.
    Body,
    /// UI labels, button text, captions.
    Label,
    /// Monospaced — code, terminal output.
    Code,
    /// Fine print, footnotes, timestamps.
    Caption,
}

/// Font weight.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FontWeight {
    Thin,       // 100
    Light,      // 300
    Regular,    // 400
    Medium,     // 500
    SemiBold,   // 600
    Bold,       // 700
    ExtraBold,  // 800
    Black,      // 900
}

impl FontWeight {
    /// Numeric CSS/platform font weight value.
    pub fn value(&self) -> u32 {
        match self {
            FontWeight::Thin => 100,
            FontWeight::Light => 300,
            FontWeight::Regular => 400,
            FontWeight::Medium => 500,
            FontWeight::SemiBold => 600,
            FontWeight::Bold => 700,
            FontWeight::ExtraBold => 800,
            FontWeight::Black => 900,
        }
    }
}

/// Text alignment — logical (RTL-aware), not physical.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TextAlign {
    /// Aligns to the start of reading direction (left in LTR, right in RTL).
    Start,
    /// Center.
    Center,
    /// Aligns to the end of reading direction.
    End,
    /// Justify.
    Justify,
}

/// Text decoration.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TextDecoration {
    None,
    Underline,
    LineThrough,
    Overline,
}

/// Overflow behavior for text that doesn't fit.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TextOverflow {
    Clip,
    Ellipsis,
    Wrap,
}

/// Fully-resolved spec for a single text style level.
#[derive(Debug, Clone)]
pub struct TextSpec {
    /// Font size in density-independent pixels.
    pub size: f32,
    /// Line height multiplier (1.5 = 150% of font size).
    pub line_height: f32,
    /// Letter spacing in em units.
    pub letter_spacing: f32,
    pub weight: FontWeight,
}

/// Maps each TextStyle level to a concrete TextSpec.
#[derive(Debug, Clone)]
pub struct TypographyScheme {
    pub display: TextSpec,
    pub headline: TextSpec,
    pub title: TextSpec,
    pub body: TextSpec,
    pub label: TextSpec,
    pub code: TextSpec,
    pub caption: TextSpec,
}

impl TypographyScheme {
    /// Default typography scale (4px base grid, 16px body).
    pub fn default() -> Self {
        Self {
            display: TextSpec {
                size: 57.0,
                line_height: 1.12,
                letter_spacing: -0.025,
                weight: FontWeight::Regular,
            },
            headline: TextSpec {
                size: 32.0,
                line_height: 1.25,
                letter_spacing: -0.015,
                weight: FontWeight::SemiBold,
            },
            title: TextSpec {
                size: 22.0,
                line_height: 1.3,
                letter_spacing: -0.01,
                weight: FontWeight::SemiBold,
            },
            body: TextSpec {
                size: 16.0,
                line_height: 1.5,
                letter_spacing: 0.0,
                weight: FontWeight::Regular,
            },
            label: TextSpec {
                size: 14.0,
                line_height: 1.4,
                letter_spacing: 0.005,
                weight: FontWeight::Medium,
            },
            code: TextSpec {
                size: 14.0,
                line_height: 1.6,
                letter_spacing: 0.0,
                weight: FontWeight::Regular,
            },
            caption: TextSpec {
                size: 12.0,
                line_height: 1.33,
                letter_spacing: 0.01,
                weight: FontWeight::Regular,
            },
        }
    }

    /// Resolve a TextStyle variant to its TextSpec.
    pub fn resolve(&self, style: &TextStyle) -> &TextSpec {
        match style {
            TextStyle::Display => &self.display,
            TextStyle::Headline => &self.headline,
            TextStyle::Title => &self.title,
            TextStyle::Body => &self.body,
            TextStyle::Label => &self.label,
            TextStyle::Code => &self.code,
            TextStyle::Caption => &self.caption,
        }
    }
}

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

    #[test]
    fn typography_scheme_body_size() {
        let scheme = TypographyScheme::default();
        let spec = scheme.resolve(&TextStyle::Body);
        assert_eq!(spec.size, 16.0);
    }

    #[test]
    fn typography_scheme_display_largest() {
        let scheme = TypographyScheme::default();
        let display = scheme.resolve(&TextStyle::Display);
        let caption = scheme.resolve(&TextStyle::Caption);
        assert!(display.size > caption.size);
    }

    #[test]
    fn font_weight_values() {
        assert_eq!(FontWeight::Regular.value(), 400);
        assert_eq!(FontWeight::Bold.value(), 700);
        assert_eq!(FontWeight::SemiBold.value(), 600);
    }

    #[test]
    fn typography_minimum_size() {
        // All text styles must be >= 11pt (accessibility minimum)
        let scheme = TypographyScheme::default();
        for style in [
            TextStyle::Display,
            TextStyle::Headline,
            TextStyle::Title,
            TextStyle::Body,
            TextStyle::Label,
            TextStyle::Code,
            TextStyle::Caption,
        ] {
            let spec = scheme.resolve(&style);
            assert!(
                spec.size >= 11.0,
                "{:?} size {} is below 11pt minimum",
                style,
                spec.size
            );
        }
    }

    #[test]
    fn typography_code_is_readable_size() {
        let scheme = TypographyScheme::default();
        let spec = scheme.resolve(&TextStyle::Code);
        assert!(spec.size >= 12.0);
    }

    #[test]
    fn typography_line_heights_positive() {
        let scheme = TypographyScheme::default();
        for style in [TextStyle::Body, TextStyle::Caption, TextStyle::Title] {
            let spec = scheme.resolve(&style);
            assert!(spec.line_height > 1.0);
        }
    }
}
