import Foundation
import Network
import Testing
@testable import VibeTunnel

// MARK: - Mock Network Utility for Testing

@MainActor
enum MockNetworkUtility {
    static var mockLocalIP: String?
    static var mockAllIPs: [String] = []
    static var shouldFailGetAddresses = false

    static func reset() {
        mockLocalIP = nil
        mockAllIPs = []
        shouldFailGetAddresses = false
    }

    static func getLocalIPAddress() -> String? {
        if shouldFailGetAddresses { return nil }
        return mockLocalIP
    }

    static func getAllIPAddresses() -> [String] {
        if shouldFailGetAddresses { return [] }
        return mockAllIPs
    }
}

// MARK: - Network Utility Tests

@Suite("Network Utility Tests", .tags(.networking))
struct NetworkUtilityTests {
    // MARK: - Local IP Address Tests

    @Test("Get local IP address")
    func testGetLocalIPAddress() throws {
        // Test real implementation
        let localIP = NetworkUtility.getLocalIPAddress()

        // On a real system, we should get some IP address
        // It might be nil in some test environments
        if let ip = localIP {
            #expect(!ip.isEmpty)

            // Should be a valid IPv4 address format
            let components = ip.split(separator: ".")
            #expect(components.count == 4)

            // Each component should be a valid number 0-255
            for component in components {
                if let num = Int(component) {
                    #expect(num >= 0 && num <= 255)
                } else {
                    Issue.record("Invalid IP component: \(component)")
                }
            }
        }
    }

    @Test("Local IP address preferences")
    func localIPPreferences() throws {
        // Test that we prefer local network addresses
        let mockIPs = [
            "192.168.1.100", // Preferred - local network
            "10.0.0.50", // Preferred - local network
            "172.16.0.10", // Preferred - local network
            "8.8.8.8", // Not preferred - public IP
            "127.0.0.1" // Should be filtered out - loopback
        ]

        // Verify our preference logic
        for ip in mockIPs {
            if ip.hasPrefix("192.168.") || ip.hasPrefix("10.") || ip.hasPrefix("172.") {
                #expect(Bool(true), "IP \(ip) should be preferred")
            }
        }
    }

    @Test("Get all IP addresses")
    func testGetAllIPAddresses() throws {
        let allIPs = NetworkUtility.getAllIPAddresses()

        // Should return array (might be empty in test environment)
        #expect(allIPs.count >= 0)

        // If we have IPs, verify they're valid
        for ip in allIPs {
            #expect(!ip.isEmpty)

            // Should not contain loopback
            #expect(!ip.hasPrefix("127."))

            // Should be valid IPv4 format
            let components = ip.split(separator: ".")
            #expect(components.count == 4)
        }
    }

    // MARK: - Network Interface Tests

    @Test("Network interface filtering")
    func interfaceFiltering() throws {
        // Test that we filter interfaces correctly
        let allIPs = NetworkUtility.getAllIPAddresses()

        // Should not contain any loopback addresses
        for ip in allIPs {
            #expect(!ip.hasPrefix("127.0.0"))
            #expect(ip != "::1") // IPv6 loopback
        }
    }

    @Test("IPv4 address validation")
    func iPv4Validation() throws {
        let testIPs = [
            ("192.168.1.1", true),
            ("10.0.0.1", true),
            ("172.16.0.1", true),
            ("256.1.1.1", false), // Invalid - component > 255
            ("1.1.1", false), // Invalid - only 3 components
            ("1.1.1.1.1", false), // Invalid - too many components
            ("a.b.c.d", false), // Invalid - non-numeric
            ("", false) // Invalid - empty
        ]

        for (ip, shouldBeValid) in testIPs {
            let components = ip.split(separator: ".")
            let isValid = components.count == 4 && components.allSatisfy { component in
                if let num = Int(component) {
                    return num >= 0 && num <= 255
                }
                return false
            }

            #expect(isValid == shouldBeValid, "IP \(ip) validation failed")
        }
    }

    // MARK: - Edge Cases Tests

    @Test("Handle no network interfaces")
    @MainActor
    func noNetworkInterfaces() throws {
        // In a real scenario where no interfaces are available
        // the functions should return nil/empty array gracefully

        MockNetworkUtility.shouldFailGetAddresses = true

        #expect(MockNetworkUtility.getLocalIPAddress() == nil)
        #expect(MockNetworkUtility.getAllIPAddresses().isEmpty)

        MockNetworkUtility.reset()
    }

    @Test("Multiple network interfaces")
    @MainActor
    func multipleInterfaces() throws {
        // When multiple interfaces exist, we should get all of them
        MockNetworkUtility.mockAllIPs = [
            "192.168.1.100", // Wi-Fi
            "192.168.2.50", // Ethernet
            "10.0.0.100" // VPN
        ]

        let allIPs = MockNetworkUtility.getAllIPAddresses()
        #expect(allIPs.count == 3)
        #expect(Set(allIPs).count == 3) // All unique

        MockNetworkUtility.reset()
    }

    // MARK: - Platform-Specific Tests

    @Test("macOS network interface names")
    func macOSInterfaceNames() throws {
        // On macOS, typical interface names are:
        // en0 - Primary network interface (often Wi-Fi)
        // en1 - Secondary network interface (often Ethernet)
        // en2, en3, etc. - Additional interfaces

        // This test documents expected behavior
        let expectedPrefixes = ["en"]

        for prefix in expectedPrefixes {
            #expect(prefix.hasPrefix("en"), "Network interfaces should start with 'en' on macOS")
        }
    }

    // MARK: - Performance Tests

    @Test("Performance of IP address retrieval", .tags(.performance))
    func iPRetrievalPerformance() async throws {
        // Measure time to get IP addresses
        let start = Date()

        for _ in 0..<10 {
            _ = NetworkUtility.getLocalIPAddress()
        }

        let elapsed = Date().timeIntervalSince(start)

        // Should be reasonably fast (< 1 second for 10 calls)
        #expect(elapsed < 1.0, "IP retrieval took too long: \(elapsed) seconds")
    }

    // MARK: - Concurrent Access Tests

    @Test("Concurrent IP address retrieval", .tags(.concurrency))
    func concurrentAccess() async throws {
        await withTaskGroup(of: String?.self) { group in
            // Multiple concurrent calls
            for _ in 0..<10 {
                group.addTask {
                    NetworkUtility.getLocalIPAddress()
                }
            }

            var results: [String?] = []
            for await result in group {
                results.append(result)
            }

            // All calls should return the same value
            let uniqueResults = Set(results.compactMap(\.self))
            #expect(uniqueResults.count <= 1, "Concurrent calls returned different IPs")
        }
    }

    // MARK: - Integration Tests

    @Test("Network utility with system network state", .tags(.integration))
    func systemNetworkState() throws {
        let localIP = NetworkUtility.getLocalIPAddress()
        let allIPs = NetworkUtility.getAllIPAddresses()

        // If we have a local IP, it should be in the all IPs list
        if let localIP {
            #expect(allIPs.contains(localIP), "Local IP should be in all IPs list")
        }

        // All IPs should be unique
        #expect(Set(allIPs).count == allIPs.count, "IP addresses should be unique")
    }

    @Test("IP address format consistency")
    func iPAddressFormat() throws {
        let allIPs = NetworkUtility.getAllIPAddresses()

        for ip in allIPs {
            // Should not have leading/trailing whitespace
            #expect(ip == ip.trimmingCharacters(in: .whitespacesAndNewlines))

            // Should not contain port numbers
            #expect(!ip.contains(":"))

            // Should be standard dotted decimal notation
            #expect(ip.contains("."))
        }
    }

    // MARK: - Mock Tests

    @Test("Mock network utility behavior")
    @MainActor
    func mockUtility() throws {
        // Set up mock
        MockNetworkUtility.mockLocalIP = "192.168.1.100"
        MockNetworkUtility.mockAllIPs = ["192.168.1.100", "10.0.0.50"]

        #expect(MockNetworkUtility.getLocalIPAddress() == "192.168.1.100")
        #expect(MockNetworkUtility.getAllIPAddresses().count == 2)

        // Test failure scenario
        MockNetworkUtility.shouldFailGetAddresses = true
        #expect(MockNetworkUtility.getLocalIPAddress() == nil)
        #expect(MockNetworkUtility.getAllIPAddresses().isEmpty)

        MockNetworkUtility.reset()
    }
}
