import Foundation

/// Supported processor types for heterogeneous computing.
public enum ProcessorType: String, Codable, CaseIterable {
    case cpu = "cpu"
    case gpu = "gpu"
    case tpu = "tpu"
    case npu = "npu"
    case lpu = "lpu"
    case fpga = "fpga"
    case dsp = "dsp"
    case webgpu = "webgpu"
    case wasm = "wasm"
    case auto = "auto"
}

/// Precision levels for compute operations.
public enum Precision: String, Codable, CaseIterable {
    case fp64 = "fp64"
    case fp32 = "fp32"
    case fp16 = "fp16"
    case bf16 = "bf16"
    case int8 = "int8"
    case int4 = "int4"
}

/// Task priority levels.
public enum Priority: String, Codable, CaseIterable {
    case critical = "critical"
    case high = "high"
    case normal = "normal"
    case low = "low"
    case background = "background"
}

/// Job execution status.
public enum JobStatus: String, Codable {
    case pending = "pending"
    case queued = "queued"
    case running = "running"
    case completed = "completed"
    case failed = "failed"
    case cancelled = "cancelled"
}

/// Model categories.
public enum ModelCategory: String, Codable, CaseIterable {
    case llm = "llm"
    case embedding = "embedding"
    case imageGeneration = "image_generation"
    case imageClassification = "image_classification"
    case objectDetection = "object_detection"
    case speechToText = "speech_to_text"
    case textToSpeech = "text_to_speech"
    case code = "code"
    case custom = "custom"
}

/// SDK configuration.
public struct SynorConfig {
    public let apiKey: String
    public var baseUrl: String
    public var defaultProcessor: ProcessorType
    public var defaultPrecision: Precision
    public var defaultPriority: Priority
    public var timeoutSeconds: TimeInterval
    public var debug: Bool

    public init(
        apiKey: String,
        baseUrl: String = "https://api.synor.io/compute/v1",
        defaultProcessor: ProcessorType = .auto,
        defaultPrecision: Precision = .fp32,
        defaultPriority: Priority = .normal,
        timeoutSeconds: TimeInterval = 30,
        debug: Bool = false
    ) {
        self.apiKey = apiKey
        self.baseUrl = baseUrl
        self.defaultProcessor = defaultProcessor
        self.defaultPrecision = defaultPrecision
        self.defaultPriority = defaultPriority
        self.timeoutSeconds = timeoutSeconds
        self.debug = debug
    }
}

/// Matrix multiplication options.
public struct MatMulOptions {
    public var precision: Precision
    public var processor: ProcessorType
    public var priority: Priority

    public init(
        precision: Precision = .fp32,
        processor: ProcessorType = .auto,
        priority: Priority = .normal
    ) {
        self.precision = precision
        self.processor = processor
        self.priority = priority
    }
}

/// Convolution options.
public struct Conv2dOptions {
    public var stride: (Int, Int)
    public var padding: (Int, Int)
    public var precision: Precision
    public var processor: ProcessorType

    public init(
        stride: (Int, Int) = (1, 1),
        padding: (Int, Int) = (0, 0),
        precision: Precision = .fp32,
        processor: ProcessorType = .auto
    ) {
        self.stride = stride
        self.padding = padding
        self.precision = precision
        self.processor = processor
    }
}

/// Attention options.
public struct AttentionOptions {
    public var numHeads: Int
    public var flash: Bool
    public var precision: Precision
    public var processor: ProcessorType

    public init(
        numHeads: Int = 8,
        flash: Bool = true,
        precision: Precision = .fp16,
        processor: ProcessorType = .gpu
    ) {
        self.numHeads = numHeads
        self.flash = flash
        self.precision = precision
        self.processor = processor
    }
}

/// Inference options.
public struct InferenceOptions {
    public var maxTokens: Int
    public var temperature: Double
    public var topP: Double
    public var topK: Int
    public var processor: ProcessorType?

    public init(
        maxTokens: Int = 256,
        temperature: Double = 0.7,
        topP: Double = 0.9,
        topK: Int = 50,
        processor: ProcessorType? = nil
    ) {
        self.maxTokens = maxTokens
        self.temperature = temperature
        self.topP = topP
        self.topK = topK
        self.processor = processor
    }
}

/// Job result.
public struct JobResult<T: Codable>: Codable {
    public let jobId: String?
    public let status: JobStatus
    public let result: T?
    public let error: String?
    public let executionTimeMs: Int64?
    public let processor: ProcessorType?
    public let cost: Double?

    public var isSuccess: Bool {
        status == .completed && error == nil
    }

    public var isFailed: Bool {
        status == .failed || error != nil
    }

    enum CodingKeys: String, CodingKey {
        case jobId = "job_id"
        case status
        case result
        case error
        case executionTimeMs = "execution_time_ms"
        case processor
        case cost
    }
}

/// Model information.
public struct ModelInfo: Codable {
    public let id: String
    public let name: String
    public let description: String?
    public let category: String
    public let parameters: Int64?
    public let contextLength: Int?
    public let format: String?
    public let recommendedProcessor: String?
    public let license: String?
    public let cid: String?

    public var formattedParameters: String {
        guard let params = parameters else { return "Unknown" }
        if params >= 1_000_000_000 {
            return "\(params / 1_000_000_000)B"
        } else if params >= 1_000_000 {
            return "\(params / 1_000_000)M"
        } else if params >= 1_000 {
            return "\(params / 1_000)K"
        }
        return "\(params)"
    }

    enum CodingKeys: String, CodingKey {
        case id, name, description, category, parameters, format, license, cid
        case contextLength = "context_length"
        case recommendedProcessor = "recommended_processor"
    }
}

/// Pricing information.
public struct PricingInfo: Codable {
    public let processor: String
    public let pricePerSecond: Double
    public let availableUnits: Int
    public let utilizationPercent: Double
    public let awsEquivalentPrice: Double?
    public let savingsPercent: Double?

    enum CodingKeys: String, CodingKey {
        case processor
        case pricePerSecond = "price_per_second"
        case availableUnits = "available_units"
        case utilizationPercent = "utilization_percent"
        case awsEquivalentPrice = "aws_equivalent_price"
        case savingsPercent = "savings_percent"
    }
}

/// Usage statistics.
public struct UsageStats: Codable {
    public let totalJobs: Int
    public let completedJobs: Int
    public let failedJobs: Int
    public let totalComputeSeconds: Double
    public let totalCost: Double

    enum CodingKeys: String, CodingKey {
        case totalJobs = "total_jobs"
        case completedJobs = "completed_jobs"
        case failedJobs = "failed_jobs"
        case totalComputeSeconds = "total_compute_seconds"
        case totalCost = "total_cost"
    }
}

/// Synor Compute error.
public enum SynorError: Error, LocalizedError {
    case invalidConfiguration(String)
    case networkError(Error)
    case apiError(Int, String)
    case decodingError(Error)
    case clientClosed

    public var errorDescription: String? {
        switch self {
        case .invalidConfiguration(let message):
            return "Invalid configuration: \(message)"
        case .networkError(let error):
            return "Network error: \(error.localizedDescription)"
        case .apiError(let code, let message):
            return "API error (\(code)): \(message)"
        case .decodingError(let error):
            return "Decoding error: \(error.localizedDescription)"
        case .clientClosed:
            return "Client has been closed"
        }
    }
}
