use crate::error::{PdfMakeError, Result};
use crate::font_subset::EmbeddedGlyph;
use crate::model::FontSource;
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;

#[derive(Debug, Clone, PartialEq)]
pub struct FontMetrics {
    pub units_per_em: u16,
    pub ascender: i16,
    pub descender: i16,
}

/// Pre-parsed glyph metrics to avoid repeated Face::parse()
#[derive(Debug, Clone)]
pub struct GlyphCache {
    /// char → (glyph_id, advance_width)
    pub char_map: HashMap<char, (u16, u16)>,
}

impl PartialEq for GlyphCache {
    fn eq(&self, other: &Self) -> bool {
        self.char_map.len() == other.char_map.len()
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct LoadedFont {
    pub family: String,
    pub data: Arc<Vec<u8>>,
    pub subset_data: Arc<Vec<u8>>,
    pub metrics: FontMetrics,
    pub glyph_cache: GlyphCache,
}

#[derive(Debug, Clone, PartialEq)]
pub struct FontRegistry {
    fonts: BTreeMap<String, LoadedFont>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct GlyphMapping {
    pub glyph_id: u16,
    pub unicode: char,
}

fn extract_first_face_from_ttc(ttc_data: &[u8]) -> Vec<u8> {
    let num_fonts = u32::from_be_bytes([ttc_data[8], ttc_data[9], ttc_data[10], ttc_data[11]]) as usize;

    struct FaceRange { start: usize, end: usize }
    let ranges: Vec<FaceRange> = (0..num_fonts).map(|i| {
        let start = u32::from_be_bytes([
            ttc_data[12 + 4*i], ttc_data[13 + 4*i],
            ttc_data[14 + 4*i], ttc_data[15 + 4*i],
        ]) as usize;
        let end = if i + 1 < num_fonts {
            u32::from_be_bytes([
                ttc_data[16 + 4*i], ttc_data[17 + 4*i],
                ttc_data[18 + 4*i], ttc_data[19 + 4*i],
            ]) as usize
        } else {
            ttc_data.len()
        };
        FaceRange { start, end }
    }).collect();

    let best = ranges.iter().max_by_key(|r| r.end - r.start).unwrap();
    ttc_data[best.start..best.end].to_vec()
}

impl FontRegistry {
    pub fn load(sources: &BTreeMap<String, FontSource>) -> Result<Self> {
        let mut fonts = BTreeMap::new();
        for (family, source) in sources {
            let data = std::fs::read(&source.path).map_err(|source_error| PdfMakeError::FontRead {
                family: family.clone(),
                path: source.path.clone(),
                source: source_error,
            })?;
            let face = ttf_parser::Face::parse(&data, 0).map_err(|_| PdfMakeError::FontParse { family: family.clone() })?;
            let metrics = FontMetrics {
                units_per_em: face.units_per_em(),
                ascender: face.ascender(),
                descender: face.descender(),
            };

            // Pre-build glyph cache: scan common character ranges
            let mut char_map = HashMap::new();
            // ASCII + Latin Extended + CJK common ranges + Punctuation + Symbols
            let ranges: &[(u32, u32)] = &[
                (0x20, 0x7E),       // ASCII printable
                (0xA0, 0xFF),       // Latin-1 Supplement
                (0x100, 0x17F),     // Latin Extended-A
                (0x180, 0x24F),     // Latin Extended-B
                (0x250, 0x2AF),     // IPA Extensions
                (0x2B0, 0x2FF),     // Spacing Modifier Letters
                (0x300, 0x36F),     // Combining Diacritical Marks
                (0x370, 0x3FF),     // Greek and Coptic
                (0x400, 0x4FF),     // Cyrillic
                (0x2000, 0x206F),   // General Punctuation (—, –, ', ', etc.)
                (0x2070, 0x209F),   // Superscripts and Subscripts
                (0x20A0, 0x20CF),   // Currency Symbols
                (0x2100, 0x214F),   // Letterlike Symbols
                (0x2190, 0x21FF),   // Arrows
                (0x2200, 0x22FF),   // Mathematical Operators
                (0x2300, 0x23FF),   // Miscellaneous Technical
                (0x2500, 0x257F),   // Box Drawing
                (0x2580, 0x259F),   // Block Elements
                (0x25A0, 0x25FF),   // Geometric Shapes
                (0x2600, 0x26FF),   // Miscellaneous Symbols
                (0x4E00, 0x9FFF),   // CJK Unified Ideographs
                (0x3400, 0x4DBF),   // CJK Extension A
                (0x3000, 0x303F),   // CJK Symbols
                (0x3040, 0x309F),   // Hiragana
                (0x30A0, 0x30FF),   // Katakana
                (0xFF00, 0xFFEF),   // Halfwidth and Fullwidth Forms
                (0xAC00, 0xD7AF),   // Hangul Syllables
            ];
            for &(start, end) in ranges {
                for codepoint in start..=end {
                    if let Some(ch) = char::from_u32(codepoint) {
                        if let Some(glyph_id) = face.glyph_index(ch) {
                            if let Some(advance) = face.glyph_hor_advance(glyph_id) {
                                char_map.insert(ch, (glyph_id.0, advance));
                            }
                        }
                    }
                }
            }

            drop(face);
            let subset_data = if data.starts_with(b"ttcf") {
                Arc::new(extract_first_face_from_ttc(&data))
            } else {
                Arc::new(data.clone())
            };
            fonts.insert(family.clone(), LoadedFont {
                family: family.clone(),
                data: Arc::new(data),
                subset_data,
                metrics,
                glyph_cache: GlyphCache { char_map },
            });
        }
        Ok(Self { fonts })
    }

    pub fn empty() -> Self {
        Self { fonts: BTreeMap::new() }
    }

    pub fn contains(&self, family: &str) -> bool {
        self.fonts.contains_key(family)
    }

    pub fn get(&self, family: &str) -> Option<&LoadedFont> {
        self.fonts.get(family)
    }

    pub fn measure(&self, family: &str, text: &str, font_size: f32) -> Result<f32> {
        let font = self.fonts.get(family).ok_or_else(|| PdfMakeError::UnknownFontFamily(family.to_string()))?;
        let mut width = 0.0f32;
        for ch in text.chars() {
            if let Some(&(_, advance)) = font.glyph_cache.char_map.get(&ch) {
                width += advance as f32 / font.metrics.units_per_em as f32 * font_size;
            } else {
                let hint = if needs_cjk_font(ch) {
                    format!(". Character '{}' is a CJK character. Register a CJK font (e.g., SimHei, NotoSansSC) using Document::builder().font(\"family\", \"path.ttf\")", ch)
                } else {
                    String::new()
                };
                return Err(PdfMakeError::MissingGlyph { family: family.to_string(), ch, hint });
            }
        }
        Ok(width)
    }

    pub fn glyphs_for_text(&self, family: &str, text: &str) -> Result<Vec<GlyphMapping>> {
        let font = self.fonts.get(family).ok_or_else(|| PdfMakeError::UnknownFontFamily(family.to_string()))?;
        text.chars()
            .map(|ch| {
                if let Some(&(glyph_id, _)) = font.glyph_cache.char_map.get(&ch) {
                    Ok(GlyphMapping { glyph_id, unicode: ch })
                } else {
                    let hint = if needs_cjk_font(ch) {
                        format!(". Character '{}' is a CJK character. Register a CJK font (e.g., SimHei, NotoSansSC) using Document::builder().font(\"family\", \"path.ttf\")", ch)
                    } else {
                        String::new()
                    };
                    Err(PdfMakeError::MissingGlyph { family: family.to_string(), ch, hint })
                }
            })
            .collect()
    }
}

pub fn build_to_unicode_cmap(family: &str, glyphs: &std::collections::BTreeSet<GlyphMapping>) -> String {
    let mut cmap = String::new();
    cmap.push_str("/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n");
    cmap.push_str(&format!("/CIDSystemInfo << /Registry ({}) /Ordering (Identity) /Supplement 0 >> def\n", family));
    cmap.push_str("/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n");
    cmap.push_str(&format!("{} beginbfchar\n", glyphs.len()));
    for glyph in glyphs {
        cmap.push_str(&format!("<{:04X}> <{:04X}>\n", glyph.glyph_id, glyph.unicode as u32));
    }
    cmap.push_str("endbfchar\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend\n");
    cmap
}

pub fn build_to_unicode_cmap_from_glyphs(family: &str, glyphs: &[EmbeddedGlyph]) -> String {
    let pairs: Vec<(u16, char)> = glyphs.iter().map(|g| (g.embedded_cid, g.unicode)).collect();
    build_to_unicode_cmap_from_pairs(family, &pairs)
}

pub fn build_to_unicode_cmap_from_pairs(family: &str, pairs: &[(u16, char)]) -> String {
    let mut cmap = String::new();
    cmap.push_str("/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n");
    cmap.push_str(&format!(
        "/CIDSystemInfo << /Registry ({}) /Ordering (Identity) /Supplement 0 >> def\n",
        family
    ));
    cmap.push_str("/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n");
    cmap.push_str(&format!("{} beginbfchar\n", pairs.len()));
    for (cid, unicode) in pairs {
        cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, *unicode as u32));
    }
    cmap.push_str("endbfchar\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend\n");
    cmap
}

pub fn needs_cjk_font(ch: char) -> bool {
    matches!(ch as u32,
        0x4E00..=0x9FFF |
        0x3400..=0x4DBF |
        0x20000..=0x2A6DF |
        0xF900..=0xFAFF |
        0x3000..=0x303F |
        0xFF00..=0xFFEF |
        0x3040..=0x309F |
        0x30A0..=0x30FF |
        0xAC00..=0xD7AF
    )
}

pub fn detect_special_chars(text: &str) -> Vec<char> {
    text.chars()
        .filter(|&ch| needs_cjk_font(ch))
        .collect()
}