// ── BLAuthClient SmartAuth v2 Tests ─────────────────────────
// Tests for social login, MFA verify methods.
// Uses URLProtocol mocking to intercept network requests.

import XCTest
@testable import ByteLystPlatformSDK

// MARK: - Mock URL Protocol

private class MockURLProtocol: URLProtocol {
    static var mockResponses: [String: (Data, Int)] = [:]

    override class func canInit(with request: URLRequest) -> Bool { true }
    override class func canonicalRequest(for request: URLRequest) -> URLRequest { request }

    override func startLoading() {
        let path = request.url?.path ?? ""
        let method = request.httpMethod ?? "GET"
        let key = "\(method) \(path)"

        if let (data, statusCode) = MockURLProtocol.mockResponses[key] {
            let response = HTTPURLResponse(
                url: request.url!,
                statusCode: statusCode,
                httpVersion: nil,
                headerFields: ["Content-Type": "application/json"]
            )!
            client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
            client?.urlProtocol(self, didLoad: data)
        } else {
            let response = HTTPURLResponse(
                url: request.url!,
                statusCode: 404,
                httpVersion: nil,
                headerFields: nil
            )!
            client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
            client?.urlProtocol(self, didLoad: Data())
        }
        client?.urlProtocolDidFinishLoading(self)
    }

    override func stopLoading() {}
}

// MARK: - Tests

final class BLAuthClientSmartAuthTests: XCTestCase {

    private var config: BLPlatformConfig!
    private var client: BLPlatformClient!
    private var authClient: BLAuthClient!

    override func setUp() {
        super.setUp()
        config = BLPlatformConfig(
            productId: "testapp",
            baseURL: "http://localhost:4003",
            platform: "ios",
            channel: "test",
            bundleId: "com.test.smartauth"
        )

        // Configure URLSession with mock protocol wired into BLPlatformClient
        let sessionConfig = URLSessionConfiguration.ephemeral
        sessionConfig.protocolClasses = [MockURLProtocol.self]

        client = BLPlatformClient(config: config, sessionConfiguration: sessionConfig)
        authClient = BLAuthClient(config: config, client: client)

        MockURLProtocol.mockResponses.removeAll()
    }

    override func tearDown() {
        MockURLProtocol.mockResponses.removeAll()
        // Clean up keychain
        BLKeychain.delete(service: "com.test.smartauth", key: "access_token")
        BLKeychain.delete(service: "com.test.smartauth", key: "refresh_token")
        super.tearDown()
    }

    // MARK: - Test 1: Swift Social Login (Google)

    func testLoginWithGoogleCallsCorrectEndpoint() async throws {
        // Mock a successful token response for Google OAuth
        let tokenJSON = """
        {"accessToken":"at_123","refreshToken":"rt_456","user":{"id":"u1","email":"test@google.com","displayName":"Test User","plan":"free","role":"user"}}
        """
        MockURLProtocol.mockResponses["POST /api/auth/oauth/google"] = (tokenJSON.data(using: .utf8)!, 200)

        let user = try await authClient.loginWithGoogle(idToken: "mock_google_id_token")
        XCTAssertEqual(user.id, "u1")
        XCTAssertEqual(user.email, "test@google.com")
        XCTAssertEqual(user.displayName, "Test User")
    }

    func testLoginWithGoogleDetectsMfaChallenge() async {
        // Mock an MFA challenge response for Google OAuth
        let mfaJSON = """
        {"mfaRequired":true,"mfaChallenge":"ch_google_123","methods":["totp"]}
        """
        MockURLProtocol.mockResponses["POST /api/auth/oauth/google"] = (mfaJSON.data(using: .utf8)!, 200)

        do {
            _ = try await authClient.loginWithGoogle(idToken: "mock_google_id_token")
            XCTFail("Should have thrown BLAuthError.mfaRequired")
        } catch let error as BLAuthError {
            if case .mfaRequired(let challenge) = error {
                XCTAssertEqual(challenge.mfaChallenge, "ch_google_123")
                XCTAssertEqual(challenge.methods, ["totp"])
            } else {
                XCTFail("Expected mfaRequired error")
            }
        }
    }

    // MARK: - Test 2: Swift MFA Verify

    func testVerifyMfaCallsCorrectEndpoint() async throws {
        // Mock a successful MFA verification response
        let tokenJSON = """
        {"accessToken":"at_mfa","refreshToken":"rt_mfa","user":{"id":"u2","email":"mfa@test.com","displayName":"MFA User"}}
        """
        MockURLProtocol.mockResponses["POST /api/auth/mfa/verify"] = (tokenJSON.data(using: .utf8)!, 200)

        let user = try await authClient.verifyMfa(
            challengeToken: "mock_challenge_token",
            code: "123456",
            method: "totp"
        )
        XCTAssertEqual(user.id, "u2")
        XCTAssertEqual(user.email, "mfa@test.com")
    }

    // MARK: - Type Verification Tests

    func testSmartAuthTypesAreDecodable() throws {
        // BLMfaChallenge
        let challengeJSON = """
        {"mfaRequired":true,"mfaChallenge":"ch_abc123","methods":["totp"]}
        """
        let challenge = try JSONDecoder().decode(BLMfaChallenge.self, from: challengeJSON.data(using: .utf8)!)
        XCTAssertTrue(challenge.mfaRequired)
        XCTAssertEqual(challenge.mfaChallenge, "ch_abc123")
        XCTAssertEqual(challenge.methods, ["totp"])

        // BLMfaStatus
        let statusJSON = """
        {"mfaEnabled":true,"methods":["totp"],"recoveryCodesRemaining":6}
        """
        let status = try JSONDecoder().decode(BLMfaStatus.self, from: statusJSON.data(using: .utf8)!)
        XCTAssertTrue(status.mfaEnabled)
        XCTAssertEqual(status.recoveryCodesRemaining, 6)

        // BLAuthProvider
        let providerJSON = """
        {"provider":"google","email":"test@test.com","linkedAt":"2026-01-01T00:00:00Z","lastUsedAt":null}
        """
        let provider = try JSONDecoder().decode(BLAuthProvider.self, from: providerJSON.data(using: .utf8)!)
        XCTAssertEqual(provider.provider, "google")
        XCTAssertNil(provider.lastUsedAt)

        // BLDevice
        let deviceJSON = """
        {"id":"dev_1","name":"iPhone 16","platform":"ios","trustLevel":"trusted","trustExpiresAt":"2026-06-01T00:00:00Z","lastLoginAt":"2026-03-01T00:00:00Z"}
        """
        let device = try JSONDecoder().decode(BLDevice.self, from: deviceJSON.data(using: .utf8)!)
        XCTAssertEqual(device.trustLevel, "trusted")
        XCTAssertEqual(device.platform, "ios")

        // BLLoginEvent
        let eventJSON = """
        {"id":"evt_1","eventType":"login_success","method":"google","ip":"1.2.3.4","geo":{"country":"US","city":"SF"},"riskScore":15,"createdAt":"2026-03-01T00:00:00Z"}
        """
        let event = try JSONDecoder().decode(BLLoginEvent.self, from: eventJSON.data(using: .utf8)!)
        XCTAssertEqual(event.riskScore, 15)
        XCTAssertEqual(event.geo?.city, "SF")
    }

    func testAuthStateIncludesMfaRequired() {
        let challenge = BLMfaChallenge(mfaRequired: true, mfaChallenge: "ch_test", methods: ["totp"])
        let state = BLAuthState.mfaRequired(challenge)

        if case .mfaRequired(let c) = state {
            XCTAssertEqual(c.mfaChallenge, "ch_test")
        } else {
            XCTFail("Expected mfaRequired state")
        }
    }

    func testBLAuthErrorMfaRequired() {
        let challenge = BLMfaChallenge(mfaRequired: true, mfaChallenge: "ch_test", methods: ["totp", "recovery"])
        let error = BLAuthError.mfaRequired(challenge)
        XCTAssertEqual(error.localizedDescription, "Multi-factor authentication required")
    }
}
