//! Разбор и генерация vCard (формат VCF).
//!
//! Поддерживаются версии 2.1, 3.0, 4.0, quoted-printable кодирование,
//! экранирование символов, группировка свойств и параметры.
//!
//! # Пример разбора
//! ```no_run
//! use maxbot::vcard::{parse_vcard, VCard};
//!
//! let vcf = "BEGIN:VCARD\nVERSION:3.0\nFN:Иван Петров\nTEL;TYPE=CELL:+71234567890\nEND:VCARD\n";
//! let card = parse_vcard(vcf).unwrap();
//! assert_eq!(card.get_formatted_name(), Some("Иван Петров"));
//! assert_eq!(card.get_phone_numbers(), vec!["+71234567890"]);
//! ```
//!
//! # Пример создания
//! ```no_run
//! use maxbot::vcard::{VCard, VCardProperty, VCardParamMap};
//!
//! let mut card = VCard::new_v3_0();
//! card.add_property(VCardProperty::new("FN", "Иван Петров"));
//! let mut tel = VCardProperty::new("TEL", "+71234567890");
//! tel.params_mut().add_param("TYPE", "CELL");
//! card.add_property(tel);
//! let vcf = card.to_string().unwrap();
//! ```

use std::collections::HashMap;
use thiserror::Error;

/// Ошибки работы с vCard.
#[derive(Debug, Error, PartialEq)]
pub enum VCardError {
    #[error("Invalid vCard: {0}")]
    InvalidFormat(String),
    #[error("Missing BEGIN:VCARD or END:VCARD")]
    MissingBoundary,
    #[error("Unsupported encoding: {0}")]
    UnsupportedEncoding(String),
    #[error("IO error: {0}")]
    Io(String),
}

type Result<T> = std::result::Result<T, VCardError>;

// -----------------------------------------------------------------------------
// Параметры vCard (мультикарта)
// -----------------------------------------------------------------------------

/// Мультикарта параметров свойства (имя → значения).
#[derive(Debug, Clone, Default)]
pub struct VCardParamMap {
    inner: HashMap<String, Vec<String>>,
}

impl VCardParamMap {
    pub fn new() -> Self {
        Self::default()
    }

    /// Добавляет значение параметра (не заменяет существующие).
    pub fn add_param(&mut self, name: &str, value: &str) {
        let key = name.to_uppercase();
        self.inner.entry(key).or_default().push(value.to_string());
    }

    /// Устанавливает параметр, заменяя все предыдущие значения.
    pub fn set_param(&mut self, name: &str, value: &str) {
        let key = name.to_uppercase();
        self.inner.insert(key, vec![value.to_string()]);
    }

    /// Удаляет все значения параметра.
    pub fn remove_param(&mut self, name: &str) {
        let key = name.to_uppercase();
        self.inner.remove(&key);
    }

    /// Проверяет наличие параметра.
    pub fn has_param(&self, name: &str) -> bool {
        let key = name.to_uppercase();
        self.inner.contains_key(&key)
    }

    /// Возвращает первое значение параметра.
    pub fn get_param(&self, name: &str) -> Option<&str> {
        let key = name.to_uppercase();
        self.inner.get(&key).and_then(|v| v.first()).map(|s| s.as_str())
    }

    /// Возвращает все значения параметра.
    pub fn get_all_params(&self, name: &str) -> Vec<&str> {
        let key = name.to_uppercase();
        self.inner.get(&key).map(|v| v.iter().map(|s| s.as_str()).collect()).unwrap_or_default()
    }

    /// Возвращает итератор по всем параметрам (имя → значения).
    pub fn iter(&self) -> impl Iterator<Item = (&String, &Vec<String>)> {
        self.inner.iter()
    }

    pub fn is_empty(&self) -> bool {
        self.inner.is_empty()
    }
}

// -----------------------------------------------------------------------------
// Свойство vCard
// -----------------------------------------------------------------------------

/// Одно свойство vCard (например, FN, TEL, N).
#[derive(Debug, Clone)]
pub struct VCardProperty {
    group: String,
    name: String,
    values: Vec<String>,
    params: VCardParamMap,
}

impl VCardProperty {
    /// Создаёт новое свойство с одним значением.
    pub fn new(name: &str, value: &str) -> Self {
        Self {
            group: String::new(),
            name: name.to_uppercase(),
            values: vec![value.to_string()],
            params: VCardParamMap::new(),
        }
    }

    /// Создаёт свойство с несколькими значениями (например, для N).
    pub fn new_multi(name: &str, values: Vec<String>) -> Self {
        Self {
            group: String::new(),
            name: name.to_uppercase(),
            values,
            params: VCardParamMap::new(),
        }
    }

    pub fn group(&self) -> &str { &self.group }
    pub fn name(&self) -> &str { &self.name }
    pub fn values(&self) -> &[String] { &self.values }
    pub fn params(&self) -> &VCardParamMap { &self.params }
    pub fn params_mut(&mut self) -> &mut VCardParamMap { &mut self.params }

    pub fn set_group(&mut self, group: &str) { self.group = group.to_string(); }
    pub fn set_name(&mut self, name: &str) { self.name = name.to_uppercase(); }
    pub fn set_values(&mut self, values: Vec<String>) { self.values = values; }

    /// Объединяет значения через ';' (для отображения).
    pub fn combined_value(&self) -> String {
        self.values.join(";")
    }
}

// -----------------------------------------------------------------------------
// Основная структура vCard
// -----------------------------------------------------------------------------

/// Представление vCard.
#[derive(Debug, Clone, Default)]
pub struct VCard {
    version: String,       // "2.1", "3.0", "4.0"
    properties: Vec<VCardProperty>,
}

impl VCard {
    pub fn new(version: &str) -> Self {
        Self {
            version: version.to_string(),
            properties: Vec::new(),
        }
    }

    pub fn new_v2_1() -> Self { Self::new("2.1") }
    pub fn new_v3_0() -> Self { Self::new("3.0") }
    pub fn new_v4_0() -> Self { Self::new("4.0") }

    pub fn version(&self) -> &str { &self.version }
    pub fn set_version(&mut self, version: &str) { self.version = version.to_string(); }

    pub fn add_property(&mut self, prop: VCardProperty) {
        self.properties.push(prop);
    }

    pub fn remove_property(&mut self, name: &str) {
        let name = name.to_uppercase();
        self.properties.retain(|p| p.name != name);
    }

    pub fn has_property(&self, name: &str) -> bool {
        let name = name.to_uppercase();
        self.properties.iter().any(|p| p.name == name)
    }

    /// Возвращает все свойства с указанным именем.
    pub fn get_properties(&self, name: &str) -> Vec<&VCardProperty> {
        let name = name.to_uppercase();
        self.properties.iter().filter(|p| p.name == name).collect()
    }

    /// Возвращает первое свойство с указанным именем.
    pub fn get_first_property(&self, name: &str) -> Option<&VCardProperty> {
        let name = name.to_uppercase();
        self.properties.iter().find(|p| p.name == name)
    }

    /// Удобные геттеры для часто используемых полей.
    pub fn get_formatted_name(&self) -> Option<&str> {
        self.get_first_property("FN").and_then(|p| p.values.first()).map(|s| s.as_str())
    }

    pub fn get_first_name(&self) -> Option<&str> {
        self.get_first_property("N").and_then(|p| p.values.get(1)).map(|s| s.as_str())
    }

    pub fn get_last_name(&self) -> Option<&str> {
        self.get_first_property("N").and_then(|p| p.values.first()).map(|s| s.as_str())
    }

    pub fn get_phone_numbers(&self) -> Vec<&str> {
        self.get_properties("TEL").iter().filter_map(|p| p.values.first()).map(|s| s.as_str()).collect()
    }

    pub fn get_emails(&self) -> Vec<&str> {
        self.get_properties("EMAIL").iter().filter_map(|p| p.values.first()).map(|s| s.as_str()).collect()
    }

    /// Сериализует vCard в строку VCF.
    pub fn to_string(&self) -> Result<String> {
        let mut out = String::new();
        out.push_str("BEGIN:VCARD\r\n");
        out.push_str(&format!("VERSION:{}\r\n", self.version));
        for prop in &self.properties {
            // Группа
            if !prop.group.is_empty() {
                out.push_str(&prop.group);
                out.push('.');
            }
            out.push_str(&prop.name);
            // Параметры
            if !prop.params.is_empty() {
                for (name, values) in prop.params.iter() {
                    for val in values {
                        out.push(';');
                        out.push_str(name);
                        out.push('=');
                        // Если значение содержит ';' или ',', оборачиваем в кавычки
                        if val.contains(';') || val.contains(',') {
                            out.push('"');
                            out.push_str(val);
                            out.push('"');
                        } else {
                            out.push_str(val);
                        }
                    }
                }
            }
            out.push(':');
            // Значения (экранируем ';' и '\')
            for (i, val) in prop.values.iter().enumerate() {
                if i > 0 { out.push(';'); }
                for c in val.chars() {
                    if c == '\\' { out.push_str("\\\\"); }
                    else if c == ';' { out.push_str("\\;"); }
                    else { out.push(c); }
                }
            }
            out.push_str("\r\n");
        }
        out.push_str("END:VCARD\r\n");
        Ok(out)
    }
}

// -----------------------------------------------------------------------------
// Обработчик vCard
// -----------------------------------------------------------------------------

/// Разбирает строку vCard и возвращает структуру VCard.
pub fn parse_vcard(vcf: &str) -> Result<VCard> {
    let lines = fold_lines(vcf);
    let mut inside = false;
    let mut card = None;

    for line in lines {
        let trimmed = line.trim();
        if trimmed.is_empty() { continue; }
        if trimmed == "BEGIN:VCARD" {
            inside = true;
            card = Some(VCard::default());
            continue;
        }
        if trimmed == "END:VCARD" {
            if let Some(c) = card.take() {
                return Ok(c);
            } else {
                return Err(VCardError::MissingBoundary);
            }
        }
        if inside {
            let card_mut = card.as_mut().unwrap();
            if let Some(prop) = parse_property(trimmed)? {
                if prop.name == "VERSION" {
                    if let Some(ver) = prop.values.first() {
                        card_mut.set_version(ver);
                    }
                } else {
                    card_mut.add_property(prop);
                }
            }
        }
    }
    Err(VCardError::MissingBoundary)
}

/// Сворачивает строки, начинающиеся с пробела/табуляции.
fn fold_lines(vcf: &str) -> Vec<String> {
    let mut result = Vec::new();
    let mut current = String::new();
    let lines: Vec<&str> = vcf.lines().map(|l| l.trim_end_matches('\r')).collect();
    let mut i = 0;
    while i < lines.len() {
        let line = lines[i];
        if current.is_empty() {
            current = line.to_string();
            i += 1;
            continue;
        }
        // Если текущая строка заканчивается на '=', то следующая строка — это продолжение (мягкий перенос)
        if current.ends_with('=') {
            // Убираем завершающий '=', он не нужен в декодировании
            current.pop();
            current.push_str(line);
            i += 1;
            continue;
        }
        // Если следующая строка начинается с пробела или табуляции — стандартное folding
        if i < lines.len() && (lines[i].starts_with(' ') || lines[i].starts_with('\t')) {
            current.push_str(&lines[i][1..]);
            i += 1;
            continue;
        }
        // Ни одно из условий — текущая строка завершена
        result.push(current);
        current = String::new();
    }
    if !current.is_empty() {
        result.push(current);
    }
    result
}

/// Разбирает одну строку свойства.
fn parse_property(line: &str) -> Result<Option<VCardProperty>> {
    // Ищем двоеточие, экранирование
    let colon_pos = find_colon_unescaped(line);
    let colon_pos = colon_pos.ok_or_else(|| VCardError::InvalidFormat(format!("Missing ':' in line: {}", line)))?;
    let head = &line[..colon_pos];
    let value_part = &line[colon_pos+1..];

    // Разбор группы
    let (group, rest) = if let Some(dot) = head.find('.') {
        (head[..dot].to_string(), &head[dot+1..])
    } else {
        (String::new(), head)
    };

    // Разбор имени и параметров
    let (name, param_str) = if let Some(semi) = rest.find(';') {
        (rest[..semi].to_uppercase(), &rest[semi+1..])
    } else {
        (rest.to_uppercase(), "")
    };

    let mut params = VCardParamMap::new();
    if !param_str.is_empty() {
        parse_params(param_str, &mut params)?;
    }

    // Разбор значений (могут быть разделены ';' с экранированием)
    let mut values = split_unescaped(value_part, ';');
    // Раскодирование quoted-printable, если требуется
    let encoding = params.get_param("ENCODING").unwrap_or("");
    if encoding == "QUOTED-PRINTABLE" {
        for v in &mut values {
            *v = decode_quoted_printable(v);
        }
    }

    // Раскодирование charset
    Ok(Some(VCardProperty {
        group,
        name,
        values,
        params,
    }))
}

/// Находит позицию первого непроэкранированного символа ':'.
fn find_colon_unescaped(s: &str) -> Option<usize> {
    let chars: Vec<char> = s.chars().collect();
    let mut i = 0;
    while i < chars.len() {
        if chars[i] == '\\' {
            i += 2; // пропускаем экранирование
            continue;
        }
        if chars[i] == ':' {
            return Some(i);
        }
        i += 1;
    }
    None
}

/// Разбивает строку по разделителю, игнорируя экранированные.
fn split_unescaped(s: &str, sep: char) -> Vec<String> {
    let mut result = Vec::new();
    let mut current = String::new();
    let mut chars = s.chars().peekable();
    while let Some(c) = chars.next() {
        if c == '\\' {
            if let Some(next) = chars.next() {
                current.push(next);
            }
        } else if c == sep {
            result.push(current);
            current = String::new();
        } else {
            current.push(c);
        }
    }
    result.push(current);
    result
}

/// Разбирает строку параметров вида "TYPE=WORK;CHARSET=UTF-8"
fn parse_params(param_str: &str, params: &mut VCardParamMap) -> Result<()> {
    for part in param_str.split(';') {
        if let Some(eq) = part.find('=') {
            let name = &part[..eq];
            let value = &part[eq+1..];
            if name == "TYPE" && value.contains(',') {
                for t in value.split(',') {
                    params.add_param(name, t);
                }
            } else {
                params.add_param(name, value);
            }
        } else {
            // параметр без значения (флаг, например, PREF, WORK)
            params.add_param(part, "");
        }
    }
    Ok(())
}

/// Раскодирует quoted-printable (простая версия).
fn decode_quoted_printable(s: &str) -> String {
    let mut out = Vec::new();
    let bytes = s.as_bytes();
    let mut i = 0;
    while i < bytes.len() {
        if bytes[i] == b'=' && i + 2 < bytes.len() {
            let hex = &bytes[i+1..i+3];
            if let Ok(byte) = u8::from_str_radix(&String::from_utf8_lossy(hex), 16) {
                out.push(byte);
                i += 3;
                continue;
            }
        }
        out.push(bytes[i]);
        i += 1;
    }
    String::from_utf8_lossy(&out).to_string()
}
