// PairingTests.swift
// Unit tests for PairingService.parseQR — IC-1 QR URL parsing.
//
// Format: pi-remote://<host>:<port>?pair=<pairingToken>&fp=<fingerprint>&name=<sidecarName>
//
// No network calls are made; parseQR is a pure function.

import XCTest
@testable import piRemote

final class PairingTests: XCTestCase {

    // =========================================================================
    // MARK: 1. Happy-path parsing
    // =========================================================================

    func testParseQR_canonical_parsesAllFields() throws {
        let url = "pi-remote://192.168.1.1:7777?pair=abc&fp=deadbeef&name=pi-remote"
        let result = try PairingService.parseQR(url)

        XCTAssertEqual(result.host,         "192.168.1.1")
        XCTAssertEqual(result.port,         7777)
        XCTAssertEqual(result.pairingToken, "abc")
        XCTAssertEqual(result.fingerprint,  "deadbeef")
        XCTAssertEqual(result.name,         "pi-remote")
    }

    func testParseQR_hostOnly_differentPort() throws {
        let url = "pi-remote://pi.local:9000?pair=tok123&fp=aabbcc&name=mypi"
        let result = try PairingService.parseQR(url)

        XCTAssertEqual(result.host,         "pi.local")
        XCTAssertEqual(result.port,         9000)
        XCTAssertEqual(result.pairingToken, "tok123")
        XCTAssertEqual(result.fingerprint,  "aabbcc")
        XCTAssertEqual(result.name,         "mypi")
    }

    func testParseQR_longFingerprint_parsedCorrectly() throws {
        let fp  = String(repeating: "a1", count: 32) // 64-char SHA-256 hex
        let url = "pi-remote://10.0.0.1:7777?pair=t&fp=\(fp)&name=n"
        let result = try PairingService.parseQR(url)
        XCTAssertEqual(result.fingerprint, fp)
    }

    func testParseQR_portBoundary_lowPort() throws {
        let url = "pi-remote://localhost:1?pair=t&fp=f&name=n"
        let result = try PairingService.parseQR(url)
        XCTAssertEqual(result.port, 1)
    }

    func testParseQR_portBoundary_highPort() throws {
        let url = "pi-remote://localhost:65535?pair=t&fp=f&name=n"
        let result = try PairingService.parseQR(url)
        XCTAssertEqual(result.port, 65535)
    }

    func testParseQR_nameWithSpaces_percentEncoded() throws {
        // Spaces encoded as %20 in the QR URL
        let url = "pi-remote://192.168.1.1:7777?pair=tok&fp=fp&name=my%20pi"
        let result = try PairingService.parseQR(url)
        XCTAssertEqual(result.name, "my pi")
    }

    // =========================================================================
    // MARK: 2. Missing required parameters → throws
    // =========================================================================

    func testParseQR_missingPairParam_throws() {
        let url = "pi-remote://192.168.1.1:7777?fp=deadbeef&name=pi-remote"
        XCTAssertThrowsError(try PairingService.parseQR(url),
                             "Missing 'pair' must throw")
    }

    func testParseQR_missingFpParam_throws() {
        let url = "pi-remote://192.168.1.1:7777?pair=abc&name=pi-remote"
        XCTAssertThrowsError(try PairingService.parseQR(url),
                             "Missing 'fp' must throw")
    }

    func testParseQR_missingNameParam_throws() {
        let url = "pi-remote://192.168.1.1:7777?pair=abc&fp=deadbeef"
        XCTAssertThrowsError(try PairingService.parseQR(url),
                             "Missing 'name' must throw")
    }

    func testParseQR_noQueryParams_throws() {
        let url = "pi-remote://192.168.1.1:7777"
        XCTAssertThrowsError(try PairingService.parseQR(url),
                             "No query params must throw")
    }

    func testParseQR_emptyPairValue_throws() {
        let url = "pi-remote://192.168.1.1:7777?pair=&fp=deadbeef&name=pi-remote"
        XCTAssertThrowsError(try PairingService.parseQR(url),
                             "Empty 'pair' value must throw")
    }

    // =========================================================================
    // MARK: 3. Wrong scheme → throws
    // =========================================================================

    func testParseQR_httpsScheme_throws() {
        // Wrong scheme — sidecar only issues pi-remote:// URLs
        let url = "https://192.168.1.1:7777?pair=abc&fp=deadbeef&name=pi-remote"
        XCTAssertThrowsError(try PairingService.parseQR(url),
                             "https:// scheme must throw .invalidQR")
    }

    func testParseQR_httpScheme_throws() {
        let url = "http://192.168.1.1:7777?pair=abc&fp=deadbeef&name=pi-remote"
        XCTAssertThrowsError(try PairingService.parseQR(url),
                             "http:// scheme must throw .invalidQR")
    }

    func testParseQR_emptyString_throws() {
        XCTAssertThrowsError(try PairingService.parseQR(""),
                             "Empty string must throw")
    }

    func testParseQR_randomString_throws() {
        XCTAssertThrowsError(try PairingService.parseQR("not-a-url"),
                             "Garbage string must throw")
    }

    // =========================================================================
    // MARK: 4. Missing port → throws
    // =========================================================================

    func testParseQR_missingPort_throws() {
        // No port specified — should throw because port is required
        let url = "pi-remote://192.168.1.1?pair=abc&fp=deadbeef&name=pi-remote"
        XCTAssertThrowsError(try PairingService.parseQR(url),
                             "Missing port must throw .invalidQR")
    }

    // =========================================================================
    // MARK: 5. Error type is PairingError.invalidQR
    // =========================================================================

    func testParseQR_wrongScheme_throwsInvalidQR() {
        XCTAssertThrowsError(try PairingService.parseQR("https://host:7777?pair=x&fp=y&name=z")) { error in
            guard let pairingError = error as? PairingError,
                  case .invalidQR = pairingError else {
                XCTFail("Expected PairingError.invalidQR, got \(error)")
                return
            }
        }
    }

    func testParseQR_missingParam_throwsInvalidQR() {
        XCTAssertThrowsError(try PairingService.parseQR("pi-remote://host:7777?pair=x&fp=y")) { error in
            guard let pairingError = error as? PairingError,
                  case .invalidQR = pairingError else {
                XCTFail("Expected PairingError.invalidQR, got \(error)")
                return
            }
        }
    }
}
