use std::fmt;
use service_kit::api_dto;

#[api_dto]
#[derive(PartialEq, Eq)]
pub enum Market {
    US,
    CN,
    HK,
    Crypto,
    Unknown,
}

impl From<&str> for Market {
    fn from(s: &str) -> Self {
        match s.to_uppercase().as_str() {
            "US" | "USA" => Market::US,
            "CN" | "CHINA" => Market::CN,
            "HK" | "HKG" => Market::HK,
            "CRYPTO" => Market::Crypto,
            _ => Market::Unknown,
        }
    }
}

/// CanonicalSymbol 是系统内部唯一的股票代码标识符类型
/// 它封装了一个标准化的字符串（遵循 Yahoo Finance 格式）
/// 使用 newtype 模式防止与普通 String 混淆
#[api_dto]
#[derive(PartialEq, Eq, Hash)]
#[serde(transparent)]
#[schema(value_type = String, example = "600519.SS")]
pub struct CanonicalSymbol(String);

impl CanonicalSymbol {
    /// 创建一个新的 CanonicalSymbol，同时执行归一化逻辑
    /// 这是从外部输入进入系统内部类型的唯一（或主要）入口
    pub fn new(input: &str, market: &Market) -> Self {
        let normalized = normalize_string(input, market);
        Self(normalized)
    }

    /// 仅用于测试或从受信任的数据库源恢复
    pub fn from_valid(symbol: String) -> Self {
        Self(symbol)
    }

    /// 获取内部字符串引用
    pub fn as_str(&self) -> &str {
        &self.0
    }

    // --- Provider Specific Conversions ---

    /// 转换为 Tushare 专用格式
    pub fn to_tushare(&self) -> String {
        // 600519.SS -> 600519.SH
        if self.0.ends_with(".SS") {
            self.0.replace(".SS", ".SH")
        } else {
            self.0.to_string()
        }
    }

    /// 转换为 Yahoo 专用格式 (即自身)
    pub fn to_yahoo(&self) -> String {
        self.0.to_string()
    }

    /// 转换为 AlphaVantage 专用格式
    /// AV 对于 A 股通常使用 .SH/.SZ，与 Tushare 类似
    pub fn to_alphavantage(&self) -> String {
        if self.0.ends_with(".SS") {
            self.0.replace(".SS", ".SH")
        } else {
            self.0.to_string()
        }
    }
}

impl fmt::Display for CanonicalSymbol {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

// 方便转回 String
impl From<CanonicalSymbol> for String {
    fn from(val: CanonicalSymbol) -> Self {
        val.0
    }
}

/// 内部归一化逻辑
fn normalize_string(input: &str, market: &Market) -> String {
    let input = input.trim().to_uppercase();
    
    // 如果已经包含后缀，暂时假设用户知道自己在做什么，或者进行简单的修正
    if input.contains('.') {
        // 处理 Tushare 特有的 .SH -> .SS 转换 (为了统一到 Yahoo 标准)
        if input.ends_with(".SH") {
            return input.replace(".SH", ".SS");
        }
        return input;
    }

    match market {
        Market::CN => {
            // A股规则推断
            if input.starts_with('6') || input.starts_with('9') {
                format!("{}.SS", input)
            } else if input.starts_with('0') || input.starts_with('3') {
                format!("{}.SZ", input)
            } else if input.starts_with('4') || input.starts_with('8') {
                 format!("{}.BJ", input)
            } else {
                format!("{}.SS", input)
            }
        },
        Market::HK => {
            // 港股补零
            let num_part = input.parse::<u32>().unwrap_or(0);
            format!("{:04}.HK", num_part)
        },
        Market::US => {
            input
        },
        Market::Crypto => {
            if !input.ends_with("-USD") {
                format!("{}-USD", input)
            } else {
                input
            }
        },
        Market::Unknown => input,
    }
}

// 保留原有函数名作为便捷入口，或者标记为 deprecated
// 为了兼容性，这里直接让他返回 CanonicalSymbol，迫使调用方修改类型
pub fn normalize_symbol(input: &str, market: &Market) -> CanonicalSymbol {
    CanonicalSymbol::new(input, market)
}

// 下面这些函数现在只是 helper，如果调用方还在用，需要保留并适配
// 推荐调用方直接使用 CanonicalSymbol 的方法
pub fn to_tushare_symbol(canonical_symbol: &str) -> String {
    CanonicalSymbol(canonical_symbol.to_string()).to_tushare()
}

pub fn to_yahoo_symbol(canonical_symbol: &str) -> String {
    canonical_symbol.to_string()
}

pub fn to_alphavantage_symbol(canonical_symbol: &str) -> String {
    CanonicalSymbol(canonical_symbol.to_string()).to_alphavantage()
}

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

    #[test]
    fn test_cn_normalization() {
        assert_eq!(CanonicalSymbol::new("600519", &Market::CN).as_str(), "600519.SS");
        assert_eq!(CanonicalSymbol::new("000001", &Market::CN).as_str(), "000001.SZ");
        assert_eq!(CanonicalSymbol::new("600519.SH", &Market::CN).as_str(), "600519.SS");
    }

    #[test]
    fn test_hk_normalization() {
        assert_eq!(CanonicalSymbol::new("700", &Market::HK).as_str(), "0700.HK");
        assert_eq!(CanonicalSymbol::new("0700", &Market::HK).as_str(), "0700.HK");
    }

    #[test]
    fn test_conversions() {
        let s = CanonicalSymbol::new("600519", &Market::CN); // 600519.SS
        assert_eq!(s.to_tushare(), "600519.SH");
        assert_eq!(s.to_alphavantage(), "600519.SH");
        assert_eq!(s.to_yahoo(), "600519.SS");
    }
}
