use crate::compiler::parser::LuaTokenKind;

enum IntegerRepr {
    Normal,
    Hex,
    Bin,
}

// Port of luaO_utf8esc from lobject.c:323-338
// Encode a code point as UTF-8, supporting extended range up to 0x7FFFFFFF
fn encode_utf8_extended(x: u32) -> Vec<u8> {
    if x < 0x80 {
        // ASCII
        vec![x as u8]
    } else {
        // Need continuation bytes
        let mut bytes = Vec::new();
        let mut x = x;
        let mut mfb = 0x3f; // maximum that fits in first byte

        // Add continuation bytes (backwards)
        loop {
            bytes.push(0x80 | (x & 0x3f) as u8);
            x >>= 6;
            mfb >>= 1;
            if x <= mfb {
                break;
            }
        }

        // Add first byte
        bytes.push(((!mfb << 1) | x) as u8);

        // Reverse to get correct order
        bytes.reverse();
        bytes
    }
}

pub enum NumberResult {
    Int(i64),
    Uint(u64),
    Float(f64),
}

pub fn parse_int_token_value(num_text: &str) -> Result<NumberResult, String> {
    let repr = if num_text.starts_with("0x") || num_text.starts_with("0X") {
        IntegerRepr::Hex
    } else if num_text.starts_with("0b") || num_text.starts_with("0B") {
        IntegerRepr::Bin
    } else {
        IntegerRepr::Normal
    };

    // 检查是否有无符号后缀并去除后缀
    let mut is_luajit_unsigned = false;
    let mut suffix_count = 0;
    for c in num_text.chars().rev() {
        if c == 'u' || c == 'U' {
            is_luajit_unsigned = true;
            suffix_count += 1;
        } else if c == 'l' || c == 'L' {
            suffix_count += 1;
        } else {
            break;
        }
    }

    let text = &num_text[..num_text.len() - suffix_count];

    // 首先尝试解析为有符号整数
    let signed_value = match repr {
        IntegerRepr::Hex => {
            let text = &text[2..];
            i64::from_str_radix(text, 16)
        }
        IntegerRepr::Bin => {
            let text = &text[2..];
            i64::from_str_radix(text, 2)
        }
        IntegerRepr::Normal => text.parse::<i64>(),
    };

    match signed_value {
        Ok(value) => Ok(NumberResult::Int(value)),
        Err(e) => {
            // 按照Lua的行为：如果整数溢出，尝试解析为浮点数
            if matches!(
                *e.kind(),
                std::num::IntErrorKind::NegOverflow | std::num::IntErrorKind::PosOverflow
            ) {
                // 如果是luajit无符号整数，尝试解析为u64
                if is_luajit_unsigned {
                    let unsigned_value = match repr {
                        IntegerRepr::Hex => {
                            let text = &text[2..];
                            u64::from_str_radix(text, 16)
                        }
                        IntegerRepr::Bin => {
                            let text = &text[2..];
                            u64::from_str_radix(text, 2)
                        }
                        IntegerRepr::Normal => text.parse::<u64>(),
                    };

                    if let Ok(value) = unsigned_value {
                        return Ok(NumberResult::Uint(value));
                    }
                } else {
                    // Lua 5.5行为：对于十六进制/二进制整数溢出，允许溢出并模2^64
                    // 这模拟了C语言中unsigned long long的自然溢出行为
                    // 例如：0xFFFFFFFFFFFFFFFF = -1
                    // 例如：0x13121110090807060504030201 = 0x807060504030201 (保留低64位)
                    if matches!(repr, IntegerRepr::Hex | IntegerRepr::Bin) {
                        let hex_str = match repr {
                            IntegerRepr::Hex => &text[2..],
                            IntegerRepr::Bin => &text[2..],
                            _ => unreachable!(),
                        };

                        // 手动解析，允许溢出（模2^64）
                        // 这与Lua 5.5中l_str2int的行为一致：
                        // for (; lisxdigit(*s); s++) {
                        //     a = a * 16 + luaO_hexavalue(*s);  // 自然溢出
                        // }
                        let base = if matches!(repr, IntegerRepr::Hex) {
                            16u64
                        } else {
                            2u64
                        };
                        let mut value = 0u64;
                        for c in hex_str.chars() {
                            if let Some(digit) = c.to_digit(base as u32) {
                                // 使用wrapping_mul和wrapping_add允许溢出
                                value = value.wrapping_mul(base).wrapping_add(digit as u64);
                            }
                        }
                        // Reinterpret u64 as i64 (补码转换)
                        return Ok(NumberResult::Int(value as i64));
                    } else if let Ok(f) = text.parse::<f64>() {
                        // 十进制整数溢出，解析为浮点数
                        return Ok(NumberResult::Float(f));
                    }
                }

                Err("malformed number".to_string())
            } else {
                Err(format!(
                    "Failed to parse integer literal '{}': {}",
                    num_text, e
                ))
            }
        }
    }
}

pub fn parse_float_token_value(num_text: &str) -> Result<f64, String> {
    let hex = num_text.starts_with("0x") || num_text.starts_with("0X");

    // This section handles the parsing of hexadecimal floating-point numbers.
    // Hexadecimal floating-point literals are of the form 0x1.8p3, where:
    // - "0x1.8" is the significand (integer and fractional parts in hexadecimal)
    // - "p3" is the exponent (in decimal, base 2 exponent)
    let value = if hex {
        let hex_float_text = &num_text[2..];
        let exponent_position = hex_float_text
            .find('p')
            .or_else(|| hex_float_text.find('P'));
        let (float_part, exponent_part) = if let Some(pos) = exponent_position {
            (&hex_float_text[..pos], &hex_float_text[(pos + 1)..])
        } else {
            (hex_float_text, "")
        };

        let (integer_part, fraction_value) = if let Some(dot_pos) = float_part.find('.') {
            let (int_part, frac_part) = float_part.split_at(dot_pos);
            let int_value = if !int_part.is_empty() {
                i64::from_str_radix(int_part, 16).unwrap_or(0)
            } else {
                0
            };
            let frac_part = &frac_part[1..];
            let frac_value = if !frac_part.is_empty() {
                let frac_part_value = i64::from_str_radix(frac_part, 16).unwrap_or(0);
                frac_part_value as f64 * 16f64.powi(-(frac_part.len() as i32))
            } else {
                0.0
            };
            (int_value, frac_value)
        } else {
            (i64::from_str_radix(float_part, 16).unwrap_or(0), 0.0)
        };

        let mut value = integer_part as f64 + fraction_value;
        if !exponent_part.is_empty()
            && let Ok(exp) = exponent_part.parse::<i32>()
        {
            value *= 2f64.powi(exp);
        }
        value
    } else {
        // For decimal float literals, use Rust's std parser which gives
        // correctly-rounded results (matching C's strtod / Lua's lua_getlocaledecpoint).
        // Splitting mantissa and exponent and computing separately introduces
        // intermediate rounding error.
        num_text
            .parse::<f64>()
            .map_err(|e| format!("Failed to parse float literal '{}': {}", num_text, e))?
    };

    Ok(value)
}

pub fn parse_string_token_value(text: &str, kind: LuaTokenKind) -> Result<Vec<u8>, String> {
    match kind {
        LuaTokenKind::TkString => normal_string_value(text),
        LuaTokenKind::TkLongString => long_string_value(text),
        _ => unreachable!(),
    }
}

fn long_string_value(text: &str) -> Result<Vec<u8>, String> {
    if text.len() < 4 {
        return Err("String too short".to_string());
    }

    let mut equal_num = 0;
    let mut i = 0;
    let mut chars = text.char_indices();

    // check first char
    if let Some((_, first_char)) = chars.next() {
        if first_char != '[' {
            return Err(format!(
                "Invalid long string start, expected '[', found '{}'",
                first_char
            ));
        }
    } else {
        return Err("Invalid long string start".to_string());
    }

    for (idx, c) in chars.by_ref() {
        // calc eq num
        if c == '=' {
            equal_num += 1;
        } else if c == '[' {
            i = idx + 1;
            break;
        } else {
            return Err(format!(
                "Invalid long string start, expected '[', found '{}'",
                c
            ));
        }
    }

    // check string len is enough
    if text.len() < i + equal_num + 2 {
        return Err("Long string too short".to_string());
    }

    // lua special rule for long string
    // Skip the first line ending if present (any form: \r, \n, \r\n, \n\r)
    if let Some((_, first_content_char)) = chars.next() {
        if first_content_char == '\r' {
            if let Some((_, next_char)) = chars.next() {
                if next_char == '\n' {
                    i += 2; // \r\n
                } else {
                    i += 1; // \r
                }
            } else {
                i += 1; // \r at end
            }
        } else if first_content_char == '\n' {
            if let Some((_, next_char)) = chars.next() {
                if next_char == '\r' {
                    i += 2; // \n\r
                } else {
                    i += 1; // \n
                }
            } else {
                i += 1; // \n at end
            }
        }
    }

    let content = &text[i..(text.len() - equal_num - 2)];

    // Port of llex.c:302-306: normalize all line endings to '\n'
    // In Lua, the following sequences are treated as a single line break:
    // \n, \r, \r\n, and \n\r
    // We need to process character by character to handle all cases correctly
    let mut normalized = Vec::with_capacity(content.len());
    let mut chars = content.chars();
    while let Some(c) = chars.next() {
        if c == '\r' {
            normalized.push(b'\n');
            // Skip following \n if present
            if let Some('\n') = chars.clone().next() {
                chars.next();
            }
        } else if c == '\n' {
            normalized.push(b'\n');
            // Skip following \r if present
            if let Some('\r') = chars.clone().next() {
                chars.next();
            }
        } else {
            // Push character as UTF-8 bytes
            let mut buf = [0u8; 4];
            let s = c.encode_utf8(&mut buf);
            normalized.extend_from_slice(s.as_bytes());
        }
    }

    Ok(normalized)
}

fn normal_string_value(text: &str) -> Result<Vec<u8>, String> {
    if text.len() < 2 {
        return Ok(Vec::new());
    }

    // Use Vec<u8> to handle raw bytes, then convert to String
    let mut result = Vec::with_capacity(text.len() - 2);
    let mut chars = text.chars().peekable();
    let delimiter = chars.next().unwrap();

    while let Some(c) = chars.next() {
        match c {
            '\\' => {
                if let Some(next_char) = chars.next() {
                    match next_char {
                        'a' => result.push(0x07),  // Bell
                        'b' => result.push(0x08),  // Backspace
                        'f' => result.push(0x0C),  // Formfeed
                        'n' => result.push(b'\n'), // Newline
                        'r' => result.push(b'\r'), // Carriage return
                        't' => result.push(b'\t'), // Horizontal tab
                        'v' => result.push(0x0B),  // Vertical tab
                        'x' => {
                            // Hexadecimal escape sequence
                            let hex = chars.by_ref().take(2).collect::<String>();
                            if hex.len() == 2 && hex.chars().all(|c| c.is_ascii_hexdigit()) {
                                if let Ok(value) = u8::from_str_radix(&hex, 16) {
                                    result.push(value);
                                }
                            } else {
                                return Err("hexadecimal digit expected".to_string());
                            }
                        }
                        'u' => {
                            // Unicode escape sequence (Lua allows up to 0x7FFFFFFF)
                            if let Some('{') = chars.next() {
                                let unicode_hex =
                                    chars.by_ref().take_while(|c| *c != '}').collect::<String>();
                                match u32::from_str_radix(&unicode_hex, 16) {
                                    Ok(code_point) => {
                                        // Lua allows UTF-8 values up to 0x7FFFFFFF (from llex.c:351)
                                        if code_point <= 0x7FFFFFFF {
                                            // Try standard Unicode first
                                            if let Some(unicode_char) =
                                                std::char::from_u32(code_point)
                                            {
                                                // Push as UTF-8 bytes
                                                let mut buf = [0u8; 4];
                                                let s = unicode_char.encode_utf8(&mut buf);
                                                result.extend_from_slice(s.as_bytes());
                                            } else {
                                                // For values > 0x10FFFF, encode as UTF-8 manually
                                                // This matches Lua's luaO_utf8esc behavior
                                                let utf8_bytes = encode_utf8_extended(code_point);
                                                result.extend_from_slice(&utf8_bytes);
                                            }
                                        } else {
                                            return Err("UTF-8 value too large".to_string());
                                        }
                                    }
                                    Err(_) => {
                                        return Err("UTF-8 value too large".to_string());
                                    }
                                }
                            }
                        }
                        '0'..='9' => {
                            // Decimal escape sequence
                            let mut dec = String::new();
                            dec.push(next_char);
                            for _ in 0..2 {
                                if let Some(digit) = chars.peek() {
                                    if digit.is_ascii_digit() {
                                        dec.push(*digit);
                                        chars.next();
                                    } else {
                                        break;
                                    }
                                }
                            }
                            if let Ok(value) = dec.parse::<u8>() {
                                result.push(value);
                            }
                        }
                        '\\' | '\'' | '\"' => result.push(next_char as u8),
                        'z' => {
                            // Skip whitespace
                            while let Some(c) = chars.peek() {
                                if !c.is_whitespace() {
                                    break;
                                }
                                chars.next();
                            }
                        }
                        '\r' => {
                            // Normalize line endings: \r, \n, \r\n, \n\r all become \n
                            // Check if next char is \n
                            if let Some(&'\n') = chars.peek() {
                                chars.next(); // consume \n
                            }
                            result.push(b'\n');
                        }
                        '\n' => {
                            // Normalize line endings: \r, \n, \r\n, \n\r all become \n
                            // Check if next char is \r
                            if let Some(&'\r') = chars.peek() {
                                chars.next(); // consume \r
                            }
                            result.push(b'\n');
                        }
                        _ => {
                            return Err(format!("invalid escape sequence '\\{}'", next_char));
                        }
                    }
                }
            }
            _ => {
                if c == delimiter {
                    break;
                }
                // Push character as UTF-8 bytes
                let mut buf = [0u8; 4];
                let s = c.encode_utf8(&mut buf);
                result.extend_from_slice(s.as_bytes());
            }
        }
    }

    // Return raw bytes - caller will decide whether to create String or Binary
    Ok(result)
}
