import Foundation
import UIKit

/**
 * DeviceContext collects and provides device/app context for analytics
 *
 * Gathers information about:
 * - Device type (phone, tablet)
 * - Operating system and version
 * - App name and version
 * - Screen dimensions
 * - Language and locale
 * - Timezone
 *
 * All values are cached at initialization for consistent reporting.
 */
internal final class DeviceContext {

    // MARK: - Model Name Mapping

    private static let modelNameMapping: [String: String] = [
        // iPhone 12
        "iPhone13,1": "iPhone 12 mini",
        "iPhone13,2": "iPhone 12",
        "iPhone13,3": "iPhone 12 Pro",
        "iPhone13,4": "iPhone 12 Pro Max",
        // iPhone 13
        "iPhone14,4": "iPhone 13 mini",
        "iPhone14,5": "iPhone 13",
        "iPhone14,2": "iPhone 13 Pro",
        "iPhone14,3": "iPhone 13 Pro Max",
        // iPhone SE (3rd gen)
        "iPhone14,6": "iPhone SE (3rd gen)",
        // iPhone 14
        "iPhone14,7": "iPhone 14",
        "iPhone14,8": "iPhone 14 Plus",
        "iPhone15,2": "iPhone 14 Pro",
        "iPhone15,3": "iPhone 14 Pro Max",
        // iPhone 15
        "iPhone15,4": "iPhone 15",
        "iPhone15,5": "iPhone 15 Plus",
        "iPhone16,1": "iPhone 15 Pro",
        "iPhone16,2": "iPhone 15 Pro Max",
        // iPhone 16
        "iPhone17,1": "iPhone 16 Pro",
        "iPhone17,2": "iPhone 16 Pro Max",
        "iPhone17,3": "iPhone 16",
        "iPhone17,4": "iPhone 16 Plus",
        "iPhone17,5": "iPhone 16e",
        // iPad Air
        "iPad13,16": "iPad Air (5th gen)",
        "iPad13,17": "iPad Air (5th gen)",
        "iPad14,8": "iPad Air (6th gen)",
        "iPad14,9": "iPad Air (6th gen)",
        "iPad14,10": "iPad Air (6th gen)",
        "iPad14,11": "iPad Air (6th gen)",
        // iPad Pro
        "iPad14,3": "iPad Pro 11-inch (4th gen)",
        "iPad14,4": "iPad Pro 11-inch (4th gen)",
        "iPad14,5": "iPad Pro 12.9-inch (6th gen)",
        "iPad14,6": "iPad Pro 12.9-inch (6th gen)",
        "iPad16,3": "iPad Pro 11-inch (M4)",
        "iPad16,4": "iPad Pro 11-inch (M4)",
        "iPad16,5": "iPad Pro 13-inch (M4)",
        "iPad16,6": "iPad Pro 13-inch (M4)",
        // iPad (standard)
        "iPad13,18": "iPad (10th gen)",
        "iPad13,19": "iPad (10th gen)",
        // iPad mini
        "iPad14,1": "iPad mini (6th gen)",
        "iPad14,2": "iPad mini (6th gen)",
        // Simulator
        "i386": "Simulator",
        "x86_64": "Simulator",
        "arm64": "Simulator",
    ]

    // MARK: - Singleton

    static let shared = DeviceContext()

    // MARK: - Device Properties

    /// Device type: "phone" or "tablet"
    let deviceType: String

    /// Device model (e.g., "iPhone14,2")
    let deviceModel: String

    /// Operating system name (always "iOS")
    let osName: String = "iOS"

    /// Operating system version (e.g., "17.0")
    let osVersion: String

    /// Screen width in points
    let screenWidth: Int

    /// Screen height in points
    let screenHeight: Int

    /// Device scale factor (e.g., 2.0 for Retina, 3.0 for Super Retina)
    let screenScale: Double

    // MARK: - App Properties

    /// App bundle identifier (e.g., "com.example.app")
    let appBundleId: String

    /// App display name
    let appName: String

    /// App version (e.g., "1.2.3")
    let appVersion: String

    /// App build number (e.g., "42")
    let appBuild: String

    // MARK: - Locale Properties

    /// User's preferred language code (e.g., "en")
    let languageCode: String

    /// User's country/region code (e.g., "US")
    let countryCode: String

    /// User's region/state (e.g., "CA", "NY") - may be empty if not available
    let region: String

    /// User's timezone identifier (e.g., "America/New_York")
    let timezone: String

    // MARK: - SDK Properties

    /// SDK version
    static let SDK_VERSION = "0.21.0"
    let sdkVersion: String = SDK_VERSION

    /// SDK platform (always "ios")
    let sdkPlatform: String = "ios"

    // MARK: - Initialization

    private init() {
        // Device type
        let idiom = UIDevice.current.userInterfaceIdiom
        self.deviceType = idiom == .pad ? "tablet" : "phone"

        // Device model (hardware identifier)
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        self.deviceModel = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8, value != 0 else { return identifier }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }

        // OS version
        self.osVersion = UIDevice.current.systemVersion

        // Screen dimensions
        let screenBounds = UIScreen.main.bounds
        self.screenWidth = Int(screenBounds.width)
        self.screenHeight = Int(screenBounds.height)
        self.screenScale = Double(UIScreen.main.scale)

        // App info from bundle
        let bundle = Bundle.main
        self.appBundleId = bundle.bundleIdentifier ?? "unknown"
        self.appName = bundle.infoDictionary?["CFBundleDisplayName"] as? String
            ?? bundle.infoDictionary?["CFBundleName"] as? String
            ?? "unknown"
        self.appVersion = bundle.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
        self.appBuild = bundle.infoDictionary?["CFBundleVersion"] as? String ?? "0"

        // Locale info (compatible with iOS 13+)
        let locale = Locale.current
        if #available(iOS 16, *) {
            self.languageCode = locale.language.languageCode?.identifier ?? "en"
            self.countryCode = locale.region?.identifier ?? "US"
        } else {
            self.languageCode = locale.languageCode ?? "en"
            self.countryCode = locale.regionCode ?? "US"
        }
        self.timezone = TimeZone.current.identifier

        // Region/state extraction - iOS doesn't provide direct access, leave empty for now
        // Could be populated from IP geolocation or user settings in future
        self.region = ""

        BCLogger.debug("DeviceContext: Initialized - \(deviceType) \(deviceModel) iOS \(osVersion)")
    }

    // MARK: - Convenience Methods

    /// Friendly device name (e.g., "iPhone 13 Pro", "iPad Air").
    /// Falls back to a generic category if the hardware identifier is unmapped.
    var friendlyDeviceName: String {
        if let name = DeviceContext.modelNameMapping[deviceModel] {
            return name
        }
        // Fallback: derive category from identifier prefix
        if deviceModel.hasPrefix("iPhone") { return "iPhone" }
        if deviceModel.hasPrefix("iPad") { return "iPad" }
        if deviceModel.hasPrefix("iPod") { return "iPod touch" }
        return deviceModel
    }

    /**
     * Get web schema compatible fields (flat structure)
     *
     * - Returns: Dictionary of flattened web schema fields
     */
    func getWebSchemaFields() -> [String: Any] {
        return [
            "browser": getBrowserField(),
            "device": friendlyDeviceName,
            "os": fullOSString,
            "country": countryCode,
            "region": region
        ]
    }

    /**
     * Get browser field for web schema (e.g., "BigCrunch iOS SDK 1.0.0")
     */
    func getBrowserField() -> String {
        return "BigCrunch iOS SDK \(sdkVersion)"
    }

    /**
     * Get mobile-specific device context fields (for optional inclusion)
     *
     * - Returns: Dictionary of mobile-specific context values
     */
    func getMobileSpecificFields() -> [String: Any] {
        return [
            "device_model": deviceModel,
            "os_version": osVersion,
            "screen_width": screenWidth,
            "screen_height": screenHeight,
            "screen_scale": screenScale,
            "app_bundle_id": appBundleId,
            "app_name": appName,
            "app_version": appVersion,
            "app_build": appBuild,
            "language_code": languageCode,
            "timezone": timezone,
            "sdk_version": sdkVersion,
            "sdk_platform": sdkPlatform
        ]
    }

    /**
     * Get a dictionary representation for analytics events (legacy format)
     * DEPRECATED: Use getWebSchemaFields() for new analytics events
     *
     * - Returns: Dictionary of device context values
     */
    func toDictionary() -> [String: Any] {
        return [
            "device_type": deviceType,
            "device_model": deviceModel,
            "os_name": osName,
            "os_version": osVersion,
            "screen_width": screenWidth,
            "screen_height": screenHeight,
            "screen_scale": screenScale,
            "app_bundle_id": appBundleId,
            "app_name": appName,
            "app_version": appVersion,
            "app_build": appBuild,
            "language_code": languageCode,
            "country_code": countryCode,
            "region": region,
            "timezone": timezone,
            "sdk_version": sdkVersion,
            "sdk_platform": sdkPlatform
        ]
    }

    /**
     * Get the full OS string (e.g., "iOS 17.0")
     */
    var fullOSString: String {
        return "\(osName) \(osVersion)"
    }

    /**
     * Get screen dimensions string (e.g., "390x844")
     */
    var screenDimensionsString: String {
        return "\(screenWidth)x\(screenHeight)"
    }
}

/**
 * Codable struct for including device context in analytics events
 */
internal struct DeviceContextData: Codable {
    let deviceType: String
    let deviceModel: String
    let osName: String
    let osVersion: String
    let screenWidth: Int
    let screenHeight: Int
    let appBundleId: String
    let appName: String
    let appVersion: String
    let languageCode: String
    let countryCode: String
    let region: String
    let timezone: String
    let sdkVersion: String
    let sdkPlatform: String

    enum CodingKeys: String, CodingKey {
        case deviceType = "device_type"
        case deviceModel = "device_model"
        case osName = "os_name"
        case osVersion = "os_version"
        case screenWidth = "screen_width"
        case screenHeight = "screen_height"
        case appBundleId = "app_bundle_id"
        case appName = "app_name"
        case appVersion = "app_version"
        case languageCode = "language_code"
        case countryCode = "country_code"
        case region = "region"
        case timezone = "timezone"
        case sdkVersion = "sdk_version"
        case sdkPlatform = "sdk_platform"
    }

    /// Create from the shared DeviceContext singleton
    static func fromShared() -> DeviceContextData {
        let ctx = DeviceContext.shared
        return DeviceContextData(
            deviceType: ctx.deviceType,
            deviceModel: ctx.deviceModel,
            osName: ctx.osName,
            osVersion: ctx.osVersion,
            screenWidth: ctx.screenWidth,
            screenHeight: ctx.screenHeight,
            appBundleId: ctx.appBundleId,
            appName: ctx.appName,
            appVersion: ctx.appVersion,
            languageCode: ctx.languageCode,
            countryCode: ctx.countryCode,
            region: ctx.region,
            timezone: ctx.timezone,
            sdkVersion: ctx.sdkVersion,
            sdkPlatform: ctx.sdkPlatform
        )
    }
}
