//! Tests for Swift code extraction.

use super::common::*;
use crate::embed::build_embedding_text;
use crate::parser::Language;

#[test]
fn test_basic_function() {
    let source = r#"func greet(name: String) -> String {
    return "Hello, \(name)!"
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    let func = get_unit_by_name(&units, "greet").unwrap();
    let text = build_embedding_text(func);

    let expected = r#"Function: greet
Signature: func greet(name: String) -> String {
Parameters: name
File: test test.swift
Code:
func greet(name: String) -> String {
    return "Hello, \(name)!"
}"#;
    assert_eq!(text, expected);
}

#[test]
fn test_function_with_doc_comment() {
    let source = r#"/// Calculates the sum of two numbers.
/// - Parameters:
///   - a: First number
///   - b: Second number
/// - Returns: Sum of a and b
func add(a: Int, b: Int) -> Int {
    return a + b
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    let func = get_unit_by_name(&units, "add").unwrap();
    let text = build_embedding_text(func);

    let expected = r#"Function: add
Signature: func add(a: Int, b: Int) -> Int {
Description: Calculates the sum of two numbers. - Parameters: - a: First number - b: Second number - Returns: Sum of a and b
Parameters: a, b
File: test test.swift
Code:
func add(a: Int, b: Int) -> Int {
    return a + b
}"#;
    assert_eq!(text, expected);
}

#[test]
fn test_class_definition() {
    let source = r#"class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func greet() -> String {
        return "Hello, I'm \(name)"
    }
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    // Class is extracted as a single chunk with all members inside
    let class = get_unit_by_name(&units, "Person").unwrap();
    let text = build_embedding_text(class);
    let expected = r#"Class: Person
Signature: class Person {
Variables: age, name
File: test test.swift
Code:
class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func greet() -> String {
        return "Hello, I'm \(name)"
    }
}"#;
    assert_eq!(text, expected);

    // Verify NO separate method unit exists
    assert!(
        get_unit_by_name(&units, "greet").is_some(),
        "Methods are extracted as separate units alongside their parent classes"
    );
}

#[test]
fn test_struct_definition() {
    let source = r#"struct Point {
    var x: Double
    var y: Double

    func distance() -> Double {
        return sqrt(x*x + y*y)
    }
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    // Struct is extracted as a single chunk with all members inside
    let class = get_unit_by_name(&units, "Point").unwrap();
    let text = build_embedding_text(class);
    let expected = r#"Class: Point
Signature: struct Point {
Calls: sqrt
Variables: x, y
File: test test.swift
Code:
struct Point {
    var x: Double
    var y: Double

    func distance() -> Double {
        return sqrt(x*x + y*y)
    }
}"#;
    assert_eq!(text, expected);

    // Verify NO separate method unit exists
    assert!(
        get_unit_by_name(&units, "distance").is_some(),
        "Methods are extracted as separate units alongside their parent structs"
    );
}

#[test]
fn test_async_function() {
    let source = r#"func fetchData(url: URL) async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    let func = get_unit_by_name(&units, "fetchData").unwrap();
    let text = build_embedding_text(func);

    let expected = r#"Function: fetchData
Signature: func fetchData(url: URL) async throws -> Data {
Parameters: url
Calls: data
File: test test.swift
Code:
func fetchData(url: URL) async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}"#;
    assert_eq!(text, expected);
}

#[test]
fn test_function_with_throws() {
    let source = r#"func parse(data: String) throws -> Int {
    guard let result = Int(data) else {
        throw ParseError.invalidFormat
    }
    return result
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    let func = get_unit_by_name(&units, "parse").unwrap();
    let text = build_embedding_text(func);

    let expected = r#"Function: parse
Signature: func parse(data: String) throws -> Int {
Parameters: data
Calls: Int
File: test test.swift
Code:
func parse(data: String) throws -> Int {
    guard let result = Int(data) else {
        throw ParseError.invalidFormat
    }
    return result
}"#;
    assert_eq!(text, expected);
}

#[test]
fn test_protocol_definition() {
    let source = r#"protocol Drawable {
    func draw()
    var bounds: CGRect { get }
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    let protocol = get_unit_by_name(&units, "Drawable").unwrap();
    let text = build_embedding_text(protocol);
    let expected = r#"Class: Drawable
Signature: protocol Drawable {
File: test test.swift
Code:
protocol Drawable {
    func draw()
    var bounds: CGRect { get }
}"#;
    assert_eq!(text, expected);
}

#[test]
fn test_enum_definition() {
    let source = r#"enum Status {
    case active
    case inactive
    case pending(reason: String)

    var description: String {
        switch self {
        case .active: return "Active"
        case .inactive: return "Inactive"
        case .pending(let reason): return "Pending: \(reason)"
        }
    }
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    let enum_unit = get_unit_by_name(&units, "Status").unwrap();
    let text = build_embedding_text(enum_unit);
    let expected = r#"Class: Status
Signature: enum Status {
Variables: description
File: test test.swift
Code:
enum Status {
    case active
    case inactive
    case pending(reason: String)

    var description: String {
        switch self {
        case .active: return "Active"
        case .inactive: return "Inactive"
        case .pending(let reason): return "Pending: \(reason)"
        }
    }
}"#;
    assert_eq!(text, expected);
}

#[test]
fn test_extension() {
    let source = r#"extension String {
    func addExclamation() -> String {
        return self + "!"
    }
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    // Extension is extracted as a single chunk with all methods inside
    let ext = get_unit_by_name(&units, "String").unwrap();
    let text = build_embedding_text(ext);
    let expected = r#"Class: String
Signature: extension String {
File: test test.swift
Code:
extension String {
    func addExclamation() -> String {
        return self + "!"
    }
}"#;
    assert_eq!(text, expected);

    // Verify NO separate method unit exists
    assert!(
        get_unit_by_name(&units, "addExclamation").is_some(),
        "Extension methods are extracted as separate units alongside their parent classes"
    );
}

#[test]
fn test_generic_function() {
    let source = r#"func swap<T>(a: inout T, b: inout T) {
    let temp = a
    a = b
    b = temp
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    let func = get_unit_by_name(&units, "swap").unwrap();
    let text = build_embedding_text(func);
    let expected = r#"Function: swap
Signature: func swap<T>(a: inout T, b: inout T) {
Parameters: a, b
Variables: temp
File: test test.swift
Code:
func swap<T>(a: inout T, b: inout T) {
    let temp = a
    a = b
    b = temp
}"#;
    assert_eq!(text, expected);
}

#[test]
fn test_property_wrapper() {
    let source = r#"@propertyWrapper
struct Clamped<Value: Comparable> {
    var wrappedValue: Value {
        didSet { wrappedValue = min(max(wrappedValue, range.lowerBound), range.upperBound) }
    }
    let range: ClosedRange<Value>
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    let class = get_unit_by_name(&units, "Clamped").unwrap();
    let text = build_embedding_text(class);
    let expected = r#"Class: Clamped
Signature: @propertyWrapper
Calls: max, min
Variables: range, wrappedValue
File: test test.swift
Code:
@propertyWrapper
struct Clamped<Value: Comparable> {
    var wrappedValue: Value {
        didSet { wrappedValue = min(max(wrappedValue, range.lowerBound), range.upperBound) }
    }
    let range: ClosedRange<Value>
}"#;
    assert_eq!(text, expected);
}

#[test]
fn test_class_inheritance() {
    let source = r#"class Animal {
    func speak() -> String {
        return "..."
    }
}

class Dog: Animal {
    override func speak() -> String {
        return "Woof!"
    }
}"#;
    let units = parse(source, Language::Swift, "test.swift");

    // Animal class is extracted as a single chunk
    let animal = get_unit_by_name(&units, "Animal").unwrap();
    let animal_text = build_embedding_text(animal);
    assert_eq!(
        animal_text,
        r#"Class: Animal
Signature: class Animal {
File: test test.swift
Code:
class Animal {
    func speak() -> String {
        return "..."
    }
}"#
    );
    // Animal has no parent
    assert!(!animal_text.contains("Extends:"));

    // Dog class is extracted as a single chunk with inheritance info
    let dog = get_unit_by_name(&units, "Dog").unwrap();
    let dog_text = build_embedding_text(dog);
    assert_eq!(
        dog_text,
        r#"Class: Dog
Signature: class Dog: Animal {
Extends: Animal
File: test test.swift
Code:
class Dog: Animal {
    override func speak() -> String {
        return "Woof!"
    }
}"#
    );

    // Verify NO separate method units exist
    assert!(
        get_unit_by_name(&units, "speak").is_some(),
        "Methods are extracted as separate units alongside their parent classes"
    );
}

#[test]
fn test_function_with_imports() {
    // Note: Swift imports frameworks (import Foundation), and types are used directly
    // without module prefix (DateFormatter() not Foundation.DateFormatter()).
    // Uses tracking doesn't apply to Swift's import pattern - similar to C# namespace imports.
    let source = r#"import Foundation

func formatDate(_ date: Date) -> String {
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    return formatter.string(from: date)
}
"#;
    let units = parse(source, Language::Swift, "test.swift");
    let func = get_unit_by_name(&units, "formatDate").unwrap();
    let text = build_embedding_text(func);

    // Swift doesn't use Module.Type pattern, so Uses won't be populated
    let expected = r#"Function: formatDate
Signature: func formatDate(_ date: Date) -> String {
Parameters: date
Calls: DateFormatter, string
Variables: formatter
File: test test.swift
Code:
func formatDate(_ date: Date) -> String {
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    return formatter.string(from: date)
}"#;
    assert_eq!(text, expected);
}
