//
//  AnalyticsManagerTests.swift
//  lichunWebsocketTests
//
//  Unit tests for AnalyticsManager
//

import XCTest
@testable import lichunWebsocket

final class AnalyticsManagerTests: XCTestCase {

    var analyticsManager: AnalyticsManager!
    var mockService: MockAnalyticsService!

    override func setUpWithError() throws {
        try super.setUpWithError()
        continueAfterFailure = false

        // Reset singleton state
        analyticsManager = AnalyticsManager.shared

        // Setup mock service
        mockService = MockAnalyticsService()
        mockService.reset()

        // Reset UserDefaults for analytics
        UserDefaults.standard.removeObject(forKey: "analyticsEnabled")
    }

    override func tearDownWithError() throws {
        mockService = nil
        UserDefaults.standard.removeObject(forKey: "analyticsEnabled")
        try super.tearDown()
    }

    // MARK: - Initialization Tests

    /// Test that AnalyticsManager initializes with correct default state
    func testInitialization() throws {
        // When analytics preference is not set, it should default to enabled
        let manager = AnalyticsManager.shared
        XCTAssertTrue(manager.isEnabled, "Analytics should be enabled by default")
    }

    /// Test that AnalyticsManager reads from UserDefaults on init
    func testInitializationWithUserDefaults() throws {
        // Given: Analytics is disabled in UserDefaults
        UserDefaults.standard.set(false, forKey: "analyticsEnabled")

        // When: Creating a new instance (singleton will use stored value)
        let isEnabled = UserDefaults.standard.bool(forKey: "analyticsEnabled")

        // Then: Should read the stored value
        XCTAssertFalse(isEnabled, "Analytics should be disabled when set in UserDefaults")
    }

    // MARK: - Event Tracking Tests

    /// Test that events are tracked when analytics is enabled
    func testEventTracking() throws {
        // Given: Analytics is enabled
        analyticsManager.setAnalyticsEnabled(true)

        // When: Tracking an event
        let event = AnalyticsEvent.characterCreated(age: 18, gender: "male")

        // Simulate event tracking by checking the event properties
        XCTAssertEqual(event.name, "character_created")
        XCTAssertEqual(event.parameters["age"] as? Int, 18)
        XCTAssertEqual(event.parameters["gender"] as? String, "male")

        // Verify manager is enabled
        XCTAssertTrue(analyticsManager.isEnabled)
    }

    /// Test that events are not tracked when analytics is disabled
    func testEventsNotTrackedWhenDisabled() throws {
        // Given: Analytics is disabled
        analyticsManager.setAnalyticsEnabled(false)

        // Then: Analytics should be disabled
        XCTAssertFalse(analyticsManager.isEnabled, "Analytics should be disabled")

        // When analytics is disabled, the track method returns early
        // Verify the isEnabled flag is false
        XCTAssertFalse(analyticsManager.isEnabled)
    }

    /// Test that multiple events can be tracked
    func testMultipleEventTracking() throws {
        // Given: Analytics is enabled
        analyticsManager.setAnalyticsEnabled(true)

        // When: Tracking multiple events
        let event1 = AnalyticsEvent.appLaunched
        let event2 = AnalyticsEvent.purchaseCompleted(itemId: "item_1", itemName: "Watch", price: 100)
        let event3 = AnalyticsEvent.onboardingComplete

        // Then: All events should have proper names
        XCTAssertEqual(event1.name, "app_launched")
        XCTAssertEqual(event2.name, "purchase_completed")
        XCTAssertEqual(event3.name, "onboarding_complete")
    }

    // MARK: - User ID Tests

    /// Test that user ID is set correctly
    func testUserIDSetting() throws {
        // Given: A user ID
        let testUserID = "test_user_123"

        // When: Setting user ID through the manager
        analyticsManager.setUserID(testUserID)

        // Then: The method completes without error
        // (Firebase call would be mocked in real implementation)
        XCTAssertNotNil(testUserID)
    }

    /// Test that user ID can be cleared
    func testUserIDClearing() throws {
        // Given: A user ID is set
        analyticsManager.setUserID("test_user_123")

        // When: Clearing the user ID
        analyticsManager.setUserID(nil)

        // Then: Method completes successfully
        // (In production, this would clear Firebase user ID)
        XCTAssertTrue(true) // Placeholder assertion
    }

    // MARK: - User Property Tests

    /// Test that user properties are set correctly
    func testUserPropertySetting() throws {
        // Given: Property name and value
        let propertyName = "subscription_tier"
        let propertyValue = "premium"

        // When: Setting user property
        analyticsManager.setUserProperty(propertyValue, forName: propertyName)

        // Then: Method completes successfully
        XCTAssertNotNil(propertyValue)
        XCTAssertNotNil(propertyName)
    }

    /// Test that user properties can handle nil values
    func testUserPropertyWithNilValue() throws {
        // Given: A property name with nil value
        let propertyName = "test_property"

        // When: Setting property to nil (to clear it)
        analyticsManager.setUserProperty(nil, forName: propertyName)

        // Then: Method completes without error
        XCTAssertTrue(true) // Placeholder assertion
    }

    // MARK: - Error Recording Tests

    /// Test that errors are recorded to Crashlytics
    func testErrorRecording() throws {
        // Given: An error to record
        let testError = NSError(
            domain: "TestDomain",
            code: 123,
            userInfo: [NSLocalizedDescriptionKey: "Test error"]
        )
        let additionalInfo = ["context": "unit_test", "test_id": "test_123"]

        // When: Recording the error
        analyticsManager.recordError(testError, additionalInfo: additionalInfo)

        // Then: Method completes successfully
        XCTAssertEqual(testError.code, 123)
        XCTAssertEqual(testError.domain, "TestDomain")
    }

    /// Test error recording without additional info
    func testErrorRecordingWithoutAdditionalInfo() throws {
        // Given: An error without additional info
        let testError = NSError(
            domain: "TestDomain",
            code: 456,
            userInfo: [NSLocalizedDescriptionKey: "Simple error"]
        )

        // When: Recording the error without additional info
        analyticsManager.recordError(testError, additionalInfo: nil)

        // Then: Method completes successfully
        XCTAssertNotNil(testError)
    }

    // MARK: - Analytics Enable/Disable Tests

    /// Test that analytics can be enabled and disabled
    func testAnalyticsEnableDisable() throws {
        // Given: Analytics is initially enabled
        analyticsManager.setAnalyticsEnabled(true)
        XCTAssertTrue(analyticsManager.isEnabled)

        // When: Disabling analytics
        analyticsManager.setAnalyticsEnabled(false)

        // Then: Analytics should be disabled
        XCTAssertFalse(analyticsManager.isEnabled)

        // When: Re-enabling analytics
        analyticsManager.setAnalyticsEnabled(true)

        // Then: Analytics should be enabled again
        XCTAssertTrue(analyticsManager.isEnabled)
    }

    // MARK: - UserDefaults Persistence Tests

    /// Test that isEnabled state persists to UserDefaults
    func testUserDefaultsPersistence() throws {
        // Given: Analytics is enabled
        analyticsManager.setAnalyticsEnabled(true)

        // Then: Value should be stored in UserDefaults
        let storedValue = UserDefaults.standard.bool(forKey: "analyticsEnabled")
        XCTAssertTrue(storedValue, "Analytics enabled state should persist to UserDefaults")

        // When: Disabling analytics
        analyticsManager.setAnalyticsEnabled(false)

        // Then: UserDefaults should be updated
        let updatedValue = UserDefaults.standard.bool(forKey: "analyticsEnabled")
        XCTAssertFalse(updatedValue, "Analytics disabled state should persist to UserDefaults")
    }

    // MARK: - Screen View Tests

    /// Test screen view tracking
    func testScreenViewTracking() throws {
        // Given: A screen name
        let screenName = "HomeScreen"
        let screenClass = "HomeViewController"

        // When: Tracking screen view
        analyticsManager.trackScreenView(screenName, screenClass: screenClass)

        // Then: Method completes successfully
        XCTAssertTrue(analyticsManager.isEnabled || !analyticsManager.isEnabled) // Just verify it runs
    }

    // MARK: - Convenience Method Tests

    /// Test app lifecycle tracking
    func testAppLifecycleTracking() throws {
        // Given: App lifecycle states
        let states = ["launched", "backgrounded", "foregrounded"]

        // When: Tracking each state
        for state in states {
            analyticsManager.trackAppLifecycle(state)
        }

        // Then: All methods complete successfully
        XCTAssertTrue(true) // Placeholder assertion
    }

    /// Test invalid lifecycle state is handled gracefully
    func testAppLifecycleInvalidState() throws {
        // Given: An invalid state
        let invalidState = "invalid_state"

        // When: Tracking invalid state
        analyticsManager.trackAppLifecycle(invalidState)

        // Then: Should handle gracefully (no crash)
        XCTAssertTrue(true)
    }

    // MARK: - Event Parameters Tests

    /// Test that event parameters are correctly constructed
    func testEventParameters() throws {
        // Test purchase event parameters
        let purchaseEvent = AnalyticsEvent.purchaseCompleted(itemId: "watch_1", itemName: "Gold Watch", price: 500)
        let purchaseParams = purchaseEvent.parameters
        XCTAssertEqual(purchaseParams["item_id"] as? String, "watch_1")
        XCTAssertEqual(purchaseParams["item_name"] as? String, "Gold Watch")
        XCTAssertEqual(purchaseParams["price"] as? Int, 500)
        XCTAssertEqual(purchaseParams["currency"] as? String, "USD")

        // Test activity event parameters
        let activityEvent = AnalyticsEvent.activityEnrolled(
            activityId: "act_123",
            activityType: "extracurricular",
            activityName: "Basketball"
        )
        let activityParams = activityEvent.parameters
        XCTAssertEqual(activityParams["activity_id"] as? String, "act_123")
        XCTAssertEqual(activityParams["activity_type"] as? String, "extracurricular")
        XCTAssertEqual(activityParams["activity_name"] as? String, "Basketball")

        // Test character death event parameters
        let deathEvent = AnalyticsEvent.characterDeath(age: 85, cause: "natural")
        let deathParams = deathEvent.parameters
        XCTAssertEqual(deathParams["age"] as? Int, 85)
        XCTAssertEqual(deathParams["cause"] as? String, "natural")
    }

    // MARK: - Log Message Tests

    /// Test breadcrumb logging
    func testBreadcrumbLogging() throws {
        // Given: A log message
        let message = "User navigated to settings"

        // When: Logging the message
        analyticsManager.log(message)

        // Then: Method completes successfully
        XCTAssertNotNil(message)
    }
}
