import Foundation
import Testing
@testable import OpenClawDiscovery

@Suite
struct TailscaleServeGatewayDiscoveryTests {
    @Test func discoversServeGatewayFromTailnetPeers() async {
        let statusJson = """
        {
          "Self": {
            "DNSName": "local-mac.tailnet-example.ts.net.",
            "HostName": "local-mac",
            "Online": true
          },
          "Peer": {
            "peer-1": {
              "DNSName": "gateway-host.tailnet-example.ts.net.",
              "HostName": "gateway-host",
              "Online": true
            },
            "peer-2": {
              "DNSName": "offline.tailnet-example.ts.net.",
              "HostName": "offline-box",
              "Online": false
            },
            "peer-3": {
              "DNSName": "local-mac.tailnet-example.ts.net.",
              "HostName": "local-mac",
              "Online": true
            }
          }
        }
        """

        let context = TailscaleServeGatewayDiscovery.DiscoveryContext(
            tailscaleStatus: { statusJson },
            probeHost: { host, _ in
                host == "gateway-host.tailnet-example.ts.net"
            })

        let beacons = await TailscaleServeGatewayDiscovery.discover(timeoutSeconds: 2.0, context: context)
        #expect(beacons.count == 1)
        #expect(beacons.first?.displayName == "gateway-host")
        #expect(beacons.first?.tailnetDns == "gateway-host.tailnet-example.ts.net")
        #expect(beacons.first?.host == "gateway-host.tailnet-example.ts.net")
        #expect(beacons.first?.port == 443)
    }

    @Test func returnsEmptyWhenStatusUnavailable() async {
        let context = TailscaleServeGatewayDiscovery.DiscoveryContext(
            tailscaleStatus: { nil },
            probeHost: { _, _ in true })

        let beacons = await TailscaleServeGatewayDiscovery.discover(timeoutSeconds: 2.0, context: context)
        #expect(beacons.isEmpty)
    }

    @Test func resolvesBareExecutableFromPATH() throws {
        let tempDir = FileManager.default.temporaryDirectory
            .appendingPathComponent(UUID().uuidString)
        try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: tempDir) }

        let executable = tempDir.appendingPathComponent("tailscale")
        try "#!/bin/sh\necho ok\n".write(to: executable, atomically: true, encoding: .utf8)
        try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: executable.path)

        let env: [String: String] = ["PATH": tempDir.path]
        let resolved = TailscaleServeGatewayDiscovery.resolveExecutablePath("tailscale", env: env)
        #expect(resolved == executable.path)
    }

    @Test func rejectsMissingExecutableCandidate() {
        #expect(TailscaleServeGatewayDiscovery.resolveExecutablePath("", env: [:]) == nil)
        #expect(TailscaleServeGatewayDiscovery.resolveExecutablePath("definitely-not-here", env: ["PATH": "/tmp"]) == nil)
    }
}
