import Foundation

/// Log severity levels (matches syslog/OpenTelemetry)
public enum DiagnosticsLogLevel: String, Codable, Sendable {
    case debug
    case info
    case warn
    case error
    case fatal
}

/// Session status from the server
public enum DiagnosticsSessionStatus: String, Codable, Sendable {
    case pending
    case active
    case paused
    case completed
    case cancelled
}

/// Collection level determines verbosity of captured data
public enum DiagnosticsCollectionLevel: String, Codable, Sendable {
    case standard
    case debug
    case trace
}

/// Diagnostic session configuration from server
public struct DiagnosticsSession: Codable, Sendable {
    public let id: String
    public let productId: String
    public let status: DiagnosticsSessionStatus
    public let collectionLevel: DiagnosticsCollectionLevel
    public let captureLogs: Bool
    public let captureNetwork: Bool
    public let captureScreenshots: Bool
    public let screenshotOnError: Bool
    public let maxDurationMinutes: Int
    public let createdAt: String
    public let expiresAt: String
    
    public init(
        id: String,
        productId: String,
        status: DiagnosticsSessionStatus,
        collectionLevel: DiagnosticsCollectionLevel,
        captureLogs: Bool,
        captureNetwork: Bool,
        captureScreenshots: Bool,
        screenshotOnError: Bool,
        maxDurationMinutes: Int,
        createdAt: String,
        expiresAt: String
    ) {
        self.id = id
        self.productId = productId
        self.status = status
        self.collectionLevel = collectionLevel
        self.captureLogs = captureLogs
        self.captureNetwork = captureNetwork
        self.captureScreenshots = captureScreenshots
        self.screenshotOnError = screenshotOnError
        self.maxDurationMinutes = maxDurationMinutes
        self.createdAt = createdAt
        self.expiresAt = expiresAt
    }
}

/// Span kind for OpenTelemetry compatibility
public enum DiagnosticsSpanKind: String, Codable, Sendable {
    case internal
    case server
    case client
    case producer
    case consumer
}

/// Span status
public enum DiagnosticsSpanStatus: String, Codable, Sendable {
    case ok
    case error
    case unset
}

/// OpenTelemetry-compatible trace span
public struct DiagnosticsTraceSpan: Codable, Sendable {
    public let spanId: String
    public let parentId: String?
    public let name: String
    public let kind: DiagnosticsSpanKind?
    public let startTime: String
    public let endTime: String?
    public let durationMs: Double?
    public let attributes: [String: AnyCodable]
    public let status: DiagnosticsSpanStatus
    public let statusMessage: String?
    
    public init(
        spanId: String,
        parentId: String? = nil,
        name: String,
        kind: DiagnosticsSpanKind? = nil,
        startTime: String,
        endTime: String? = nil,
        durationMs: Double? = nil,
        attributes: [String: AnyCodable] = [:],
        status: DiagnosticsSpanStatus,
        statusMessage: String? = nil
    ) {
        self.spanId = spanId
        self.parentId = parentId
        self.name = name
        self.kind = kind
        self.startTime = startTime
        self.endTime = endTime
        self.durationMs = durationMs
        self.attributes = attributes
        self.status = status
        self.statusMessage = statusMessage
    }
}

/// Structured log entry
public struct DiagnosticsLogEntry: Codable, Sendable {
    public let level: DiagnosticsLogLevel
    public let message: String
    public let timestamp: String
    public let module: String
    public let file: String?
    public let line: Int?
    public let function: String?
    public let context: [String: AnyCodable]
    public let correlationId: String?
    
    public init(
        level: DiagnosticsLogLevel,
        message: String,
        timestamp: String,
        module: String,
        file: String? = nil,
        line: Int? = nil,
        function: String? = nil,
        context: [String: AnyCodable] = [:],
        correlationId: String? = nil
    ) {
        self.level = level
        self.message = message
        self.timestamp = timestamp
        self.module = module
        self.file = file
        self.line = line
        self.function = function
        self.context = context
        self.correlationId = correlationId
    }
}

/// Breadcrumb for timeline navigation
public struct DiagnosticsBreadcrumb: Codable, Sendable {
    public let timestamp: String
    public let category: String
    public let message: String
    public let data: [String: AnyCodable]?
    
    public init(
        timestamp: String,
        category: String,
        message: String,
        data: [String: AnyCodable]? = nil
    ) {
        self.timestamp = timestamp
        self.category = category
        self.message = message
        self.data = data
    }
}

/// Network request/response capture
public struct DiagnosticsNetworkRequest: Codable, Sendable {
    public let id: String
    public let url: String
    public let method: String
    public let requestHeaders: [String: String]
    public let requestBody: String?
    public let status: Int?
    public let responseHeaders: [String: String]?
    public let responseBody: String?
    public let startTime: String
    public let endTime: String?
    public let durationMs: Double?
    public let error: String?
    
    public init(
        id: String,
        url: String,
        method: String,
        requestHeaders: [String: String] = [:],
        requestBody: String? = nil,
        status: Int? = nil,
        responseHeaders: [String: String]? = nil,
        responseBody: String? = nil,
        startTime: String,
        endTime: String? = nil,
        durationMs: Double? = nil,
        error: String? = nil
    ) {
        self.id = id
        self.url = url
        self.method = method
        self.requestHeaders = requestHeaders
        self.requestBody = requestBody
        self.status = status
        self.responseHeaders = responseHeaders
        self.responseBody = responseBody
        self.startTime = startTime
        self.endTime = endTime
        self.durationMs = durationMs
        self.error = error
    }
}

/// Device state snapshot
public struct DiagnosticsDeviceState: Codable, Sendable {
    public let memoryMB: Int?
    public let batteryLevel: Double?
    public let isCharging: Bool?
    public let storageMB: Int?
    public let networkType: String?
    public let isOnline: Bool
    public let thermalState: DiagnosticsThermalState?
    
    public init(
        memoryMB: Int? = nil,
        batteryLevel: Double? = nil,
        isCharging: Bool? = nil,
        storageMB: Int? = nil,
        networkType: String? = nil,
        isOnline: Bool,
        thermalState: DiagnosticsThermalState? = nil
    ) {
        self.memoryMB = memoryMB
        self.batteryLevel = batteryLevel
        self.isCharging = isCharging
        self.storageMB = storageMB
        self.networkType = networkType
        self.isOnline = isOnline
        self.thermalState = thermalState
    }
}

/// Thermal state
public enum DiagnosticsThermalState: String, Codable, Sendable {
    case nominal
    case fair
    case serious
    case critical
}

/// Client state
public enum DiagnosticsClientState: Sendable {
    case idle
    case polling(session: DiagnosticsSession?)
    case active(session: DiagnosticsSession)
    case error(Error)
}

/// Ingest batch for sending to server
public struct DiagnosticsIngestBatch: Codable, Sendable {
    public let sessionId: String
    public let traces: [DiagnosticsTraceSpan]?
    public let logs: [DiagnosticsLogEntry]?
    public let breadcrumbs: [DiagnosticsBreadcrumb]?
    public let network: [DiagnosticsNetworkRequest]?
    
    public init(
        sessionId: String,
        traces: [DiagnosticsTraceSpan]? = nil,
        logs: [DiagnosticsLogEntry]? = nil,
        breadcrumbs: [DiagnosticsBreadcrumb]? = nil,
        network: [DiagnosticsNetworkRequest]? = nil
    ) {
        self.sessionId = sessionId
        self.traces = traces
        self.logs = logs
        self.breadcrumbs = breadcrumbs
        self.network = network
    }
}

/// Type-erased Codable wrapper for dictionary values
public struct AnyCodable: Codable, Sendable {
    private let value: any Codable
    
    public init(_ value: any Codable) {
        self.value = value
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let boolValue = try? container.decode(Bool.self) {
            value = boolValue
        } else if let intValue = try? container.decode(Int.self) {
            value = intValue
        } else if let doubleValue = try? container.decode(Double.self) {
            value = doubleValue
        } else if let stringValue = try? container.decode(String.self) {
            value = stringValue
        } else if let arrayValue = try? container.decode([AnyCodable].self) {
            value = arrayValue
        } else if let dictValue = try? container.decode([String: AnyCodable].self) {
            value = dictValue
        } else {
            value = ""
        }
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        if let boolValue = value as? Bool {
            try container.encode(boolValue)
        } else if let intValue = value as? Int {
            try container.encode(intValue)
        } else if let doubleValue = value as? Double {
            try container.encode(doubleValue)
        } else if let stringValue = value as? String {
            try container.encode(stringValue)
        } else if let arrayValue = value as? [AnyCodable] {
            try container.encode(arrayValue)
        } else if let dictValue = value as? [String: AnyCodable] {
            try container.encode(dictValue)
        } else {
            try container.encode("")
        }
    }
}
