import Foundation

#if canImport(Darwin)
import Darwin
#elseif canImport(Musl)
import Musl
#else
import Glibc
#endif

// The libc socket surface differs in spelling across the three C libraries nucleicd builds
// against (Darwin / Glibc / Musl) — same aliases as MCPApprovalServer's TCP leg.
#if canImport(Darwin)
private let sockStreamType: Int32 = SOCK_STREAM
private func posixBind(_ fd: Int32, _ addr: UnsafePointer<sockaddr>, _ len: socklen_t) -> Int32 {
    Darwin.bind(fd, addr, len)
}
private func posixCloseFD(_ fd: Int32) { _ = Darwin.close(fd) }
private func writeNoSigpipe(_ fd: Int32, _ buf: UnsafeRawPointer, _ count: Int) -> Int {
    write(fd, buf, count)  // SO_NOSIGPIPE is set on the accepted fd
}
#elseif canImport(Musl)
private let sockStreamType: Int32 = Int32(SOCK_STREAM)
private func posixBind(_ fd: Int32, _ addr: UnsafePointer<sockaddr>, _ len: socklen_t) -> Int32 {
    Musl.bind(fd, addr, len)
}
private func posixCloseFD(_ fd: Int32) { _ = Musl.close(fd) }
private func writeNoSigpipe(_ fd: Int32, _ buf: UnsafeRawPointer, _ count: Int) -> Int {
    // Linux has no SO_NOSIGPIPE; MSG_NOSIGNAL per send is the localized equivalent.
    send(fd, buf, count, Int32(MSG_NOSIGNAL))
}
#else
private let sockStreamType = Int32(SOCK_STREAM.rawValue)
private func posixBind(_ fd: Int32, _ addr: UnsafePointer<sockaddr>, _ len: socklen_t) -> Int32 {
    Glibc.bind(fd, addr, len)
}
private func posixCloseFD(_ fd: Int32) { _ = Glibc.close(fd) }
private func writeNoSigpipe(_ fd: Int32, _ buf: UnsafeRawPointer, _ count: Int) -> Int {
    // Linux has no SO_NOSIGPIPE; MSG_NOSIGNAL per send is the localized equivalent.
    send(fd, buf, count, Int32(MSG_NOSIGNAL))
}
#endif

/// The runner's loopback control endpoint (docs/ANTIMATTER_RUNNER.md §0.2 item 3d, §11.3): a
/// tiny HTTP listener on `127.0.0.1:<port>` (default 9200 — `NUCLEICD_CONTROL_PORT` in
/// cloud/nucleic-runner/src/container.ts) that the `RunnerContainer` DO proxies into. Never
/// exposed beyond loopback; inside Cloudflare Containers it is only reachable through the
/// Worker's authenticated `GET /v1/pool/pairing` route.
///
///   GET /pairing  → the one-shot `nucleic://pair?d=…` payload for the provisioning channel:
///                   mint → serve → **burn** (subsequent calls answer 410 Gone), mirroring
///                   `--print-pairing`'s single stdout line. Re-provisioning means a container
///                   restart — the pairing secret is one-time by design.
///   GET /health   → 200 "ok" (local liveness for the container runtime).
///
/// Single-threaded accept loop on a detached task — the proxy makes one request at a time and
/// there is nothing concurrent to win here. `stop()` closes the listener, which unblocks the
/// loop.
final class ControlEndpoint: @unchecked Sendable {
    private let port: UInt16
    private let mintPairing: @Sendable () async -> String?

    private let lock = NSLock()
    private var listenFD: Int32 = -1
    private var pairingServed = false
    private var acceptLoop: Task<Void, Never>?

    init(port: UInt16, mintPairing: @escaping @Sendable () async -> String?) {
        self.port = port
        self.mintPairing = mintPairing
    }

    struct ControlEndpointError: Error, CustomStringConvertible {
        let description: String
    }

    /// Bind + listen on loopback, then serve until `stop()`. Throws if the port can't be bound.
    func start() throws {
        let fd = socket(AF_INET, sockStreamType, 0)
        guard fd >= 0 else {
            throw ControlEndpointError(description: "socket() failed: \(String(cString: strerror(errno)))")
        }
        _ = fcntl(fd, F_SETFD, FD_CLOEXEC)
        var one: Int32 = 1
        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, socklen_t(MemoryLayout<Int32>.size))

        var addr = sockaddr_in()
        addr.sin_family = sa_family_t(AF_INET)
        addr.sin_port = port.bigEndian
        addr.sin_addr.s_addr = inet_addr("127.0.0.1")
        let bound = withUnsafePointer(to: &addr) { ptr in
            ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { sa in
                posixBind(fd, sa, socklen_t(MemoryLayout<sockaddr_in>.size))
            }
        }
        guard bound == 0 else {
            posixCloseFD(fd)
            throw ControlEndpointError(description: "bind(127.0.0.1:\(port)) failed: \(String(cString: strerror(errno)))")
        }
        guard listen(fd, 4) == 0 else {
            posixCloseFD(fd)
            throw ControlEndpointError(description: "listen failed: \(String(cString: strerror(errno)))")
        }

        lock.lock()
        listenFD = fd
        lock.unlock()

        acceptLoop = Task.detached(priority: .utility) { [weak self] in
            while let self, !Task.isCancelled {
                let client = accept(fd, nil, nil)
                guard client >= 0 else { break }  // listener closed (stop()) or fatal
                #if canImport(Darwin)
                var one: Int32 = 1
                setsockopt(client, SOL_SOCKET, SO_NOSIGPIPE, &one, socklen_t(MemoryLayout<Int32>.size))
                #endif
                await self.serve(client)
                posixCloseFD(client)
            }
        }
    }

    func stop() {
        acceptLoop?.cancel()
        lock.lock()
        let fd = listenFD
        listenFD = -1
        lock.unlock()
        if fd >= 0 { posixCloseFD(fd) }
    }

    // MARK: - One request

    private func serve(_ fd: Int32) async {
        // One read is enough: both requests are tiny GETs and the proxy sends no body.
        var buffer = [UInt8](repeating: 0, count: 2048)
        let n = read(fd, &buffer, buffer.count)
        guard n > 0, let head = String(bytes: buffer[0..<n], encoding: .utf8),
              let requestLine = head.split(separator: "\r\n", maxSplits: 1).first
        else {
            respond(fd, status: "400 Bad Request", body: "bad request\n")
            return
        }

        switch requestLine.split(separator: " ").prefix(2).joined(separator: " ") {
        case "GET /health":
            respond(fd, status: "200 OK", body: "ok\n")
        case "GET /pairing":
            if isPairingServed() {
                respond(fd, status: "410 Gone", body: "pairing already served — restart the runner to re-provision\n")
                return
            }
            guard let code = await mintPairing() else {
                respond(fd, status: "503 Service Unavailable", body: "pairing unavailable (sync server not accepting?)\n")
                return
            }
            // Burn before the bytes go out: a half-delivered payload must count as served —
            // the secret may already be in flight, and double-arming is worse than a restart.
            burnPairing()
            respond(fd, status: "200 OK", body: code + "\n")
        default:
            respond(fd, status: "404 Not Found", body: "not found\n")
        }
    }

    // Sync accessors for the one-shot flag — `NSLock.lock()` is unavailable directly inside
    // async bodies, so the async `serve` goes through these.
    private func isPairingServed() -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return pairingServed
    }

    private func burnPairing() {
        lock.lock()
        pairingServed = true
        lock.unlock()
    }

    private func respond(_ fd: Int32, status: String, body: String) {
        let payload = Data(body.utf8)
        let head = "HTTP/1.1 \(status)\r\n"
            + "content-type: text/plain; charset=utf-8\r\n"
            + "content-length: \(payload.count)\r\n"
            + "connection: close\r\n\r\n"
        var out = Data(head.utf8)
        out.append(payload)
        out.withUnsafeBytes { raw in
            guard let base = raw.baseAddress else { return }
            var offset = 0
            while offset < raw.count {
                let written = writeNoSigpipe(fd, base + offset, raw.count - offset)
                if written > 0 { offset += written } else if written < 0 && errno == EINTR { continue } else { break }
            }
        }
    }
}
