use std::num::NonZeroU32;

use krilla::page::{NumberingStyle, PageLabel};
use typst_library::model::Numbering;

pub(crate) trait PageLabelExt {
    /// Create a new `PageLabel` from a `Numbering` applied to a page
    /// number.
    fn generate(numbering: &Numbering, number: u64) -> Option<PageLabel>;

    /// Creates an arabic page label with the specified page number.
    /// For example, this will display page label `11` when given the page
    /// number 11.
    fn arabic(number: u64) -> PageLabel;
}

impl PageLabelExt for PageLabel {
    fn generate(numbering: &Numbering, number: u64) -> Option<PageLabel> {
        let Numbering::Pattern(pat) = numbering else {
            return None;
        };

        let (prefix, system) = pat.pieces.first()?;

        // If there is a suffix, we cannot use the common style optimization,
        // since PDF does not provide a suffix field.
        let style = if pat.suffix.is_empty() {
            use codex::numeral_systems::NamedNumeralSystem as System;
            use krilla::page::NumberingStyle as Style;
            match system {
                System::Arabic => Some(Style::Arabic),
                System::LowerRoman => Some(Style::LowerRoman),
                System::UpperRoman => Some(Style::UpperRoman),
                System::LowerLatin if number <= 26 => Some(Style::LowerAlpha),
                System::UpperLatin if number <= 26 => Some(Style::UpperAlpha),
                _ => None,
            }
        } else {
            None
        };

        // Prefix and offset depend on the style: If it is supported by the PDF
        // spec, we use the given prefix and an offset. Otherwise, everything
        // goes into prefix.
        let prefix = if style.is_none() {
            pat.apply(None, &[number]).ok()
        } else {
            (!prefix.is_empty()).then(|| prefix.clone())
        };

        let offset = style.and(number.try_into().ok().and_then(NonZeroU32::new));
        Some(PageLabel::new(style, prefix.map(Into::into), offset))
    }

    fn arabic(number: u64) -> PageLabel {
        PageLabel::new(
            Some(NumberingStyle::Arabic),
            None,
            number.try_into().ok().and_then(NonZeroU32::new),
        )
    }
}
