//! TypeScript instant transpilation (lightweight regex-based).
//!
//! Strips TypeScript type annotations without full AST parsing.
//! Handles common patterns for Vue SFC `<script lang="ts">` blocks.

use regex::Regex;

/// Strip TypeScript type annotations from source code.
///
/// Returns the transformed JavaScript with all TS-specific syntax removed.
/// Uses multiple regex passes for different TS features.
pub fn strip_types(source: &str) -> String {
    let mut result = source.to_string();

    // 1. 移除 import type 语句
    let re_import_type = Regex::new(r"(?m)^\s*import\s+type\s+.*$").unwrap();
    result = re_import_type.replace_all(&result, "").to_string();

    // 2. 移除 export type 语句
    let re_export_type = Regex::new(r"(?m)^\s*export\s+type\s+.*$").unwrap();
    result = re_export_type.replace_all(&result, "").to_string();

    // 3. 移除 interface 声明（跨行）
    let re_interface = Regex::new(r"(?ms)^\s*interface\s+\w+\s*(?:extends\s+[\w,\s]+)?\s*\{[^}]*\}").unwrap();
    result = re_interface.replace_all(&result, "").to_string();

    // 4. 移除 type 别名（单行）
    let re_type = Regex::new(r"(?m)^\s*type\s+\w+(?:<[^>]*>)?\s*=.*[;]?\s*$").unwrap();
    result = re_type.replace_all(&result, "").to_string();

    // 5. 移除函数参数中的 : Type 注解
    let re_param_type = Regex::new(r":\s*(?:string|number|boolean|void|null|undefined|any|never|bigint|symbol)\b").unwrap();
    result = re_param_type.replace_all(&result, "").to_string();

    // 6. 移除 : Type（更通用的，在变量声明中）
    let re_var_type = Regex::new(r"(?m)^(\s*(?:const|let|var)\s+\w+)\s*:\s*[^=;]+(=)").unwrap();
    result = re_var_type.replace_all(&result, "$1 $2").to_string();

    // 7. 移除函数返回值 : Type
    let re_return_type = Regex::new(r"(\))\s*:\s*(?:string|number|boolean|void|Promise<[^>]*>|[A-Z]\w*(?:<[^>]*>)?)\s*(\{)").unwrap();
    result = re_return_type.replace_all(&result, "$1 $2").to_string();

    // 8. 移除 as Type
    let re_as = Regex::new(r"\s+as\s+(?:string|number|boolean|any|void|null|undefined|never|[A-Z]\w*(?:<[^>]*>)?)\b").unwrap();
    result = re_as.replace_all(&result, "").to_string();

    // 9. 移除泛型参数 <T, U> 等（在函数名后、变量后的尖括号）
    let re_generics = Regex::new(r"(?P<name>\b\w+)\s*<(\s*(?:[A-Z]\w*(?:\s+extends\s+\w+)?)\s*(?:,\s*(?:[A-Z]\w*(?:\s+extends\s+\w+)?)\s*)*)>").unwrap();
    result = re_generics.replace_all(&result, "$name").to_string();

    // 清理多余空白
    let re_blank_lines = Regex::new(r"\n\s*\n\s*\n").unwrap();
    result = re_blank_lines.replace_all(&result, "\n\n").to_string();

    // 清理多余空格
    let re_spaces = Regex::new(r" {2,}").unwrap();
    result = re_spaces.replace_all(&result, " ").to_string();

    result.trim().to_string()
}

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

    #[test]
    fn test_strip_type_annotations() {
        let input = "const x: number = 42";
        let output = strip_types(input);
        assert_eq!(output, "const x = 42");
    }

    #[test]
    fn test_strip_function_return_type() {
        let input = "function add(a: string, b: number): number { return a + b; }";
        let output = strip_types(input);
        assert_eq!(output, "function add(a, b) { return a + b; }");
    }

    #[test]
    fn test_strip_as_expression() {
        let input = "let x = y as number;";
        let output = strip_types(input);
        assert_eq!(output, "let x = y;");
    }

    #[test]
    fn test_strip_interface() {
        let input = "interface Props { name: string; age?: number; }";
        let output = strip_types(input);
        assert_eq!(output, "");
    }

    #[test]
    fn test_strip_import_type() {
        let input = "import type { Foo } from './foo';";
        let output = strip_types(input);
        assert_eq!(output, "");
    }

    #[test]
    fn test_preserve_regular_code() {
        let input = "const x = 42; function foo() { return x; }";
        let output = strip_types(input);
        assert_eq!(output, input);
    }

    #[test]
    fn test_strip_generics() {
        let input = "function identity<T>(x: T): T { return x; }";
        let output = strip_types(input);
        assert!(output.contains("function identity"));
        assert!(output.contains("return x;"));
    }

    #[test]
    fn test_strip_arrow_function_types() {
        let input = "const fn = (x: string): string => String(x);";
        let output = strip_types(input);
        assert_eq!(output, "const fn = (x) => String(x);");
    }

    #[test]
    fn test_strip_type_aliases() {
        let input = "type MyType = string;\nconst x: MyType = 'hello';";
        let output = strip_types(input);
        assert!(!output.contains("type MyType"));
    }

    #[test]
    fn test_vue_script_setup() {
        let input = r#"
<script setup lang="ts">
import { ref } from 'vue';
import type { PropType } from 'vue';

interface Props {
  title: string;
  count?: number;
}

const props = defineProps<Props>();
const msg: string = 'hello';
</script>"#;
        let output = strip_types(input);
        assert!(output.contains("import { ref }"));
        assert!(!output.contains("import type"));
        assert!(!output.contains("interface Props"));
        assert!(output.contains("const msg"));
    }
}