package io.synor.compute

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
 * Supported processor types for heterogeneous computing.
 */
@Serializable
enum class ProcessorType(val value: String) {
    @SerialName("cpu") CPU("cpu"),
    @SerialName("gpu") GPU("gpu"),
    @SerialName("tpu") TPU("tpu"),
    @SerialName("npu") NPU("npu"),
    @SerialName("lpu") LPU("lpu"),
    @SerialName("fpga") FPGA("fpga"),
    @SerialName("dsp") DSP("dsp"),
    @SerialName("webgpu") WEBGPU("webgpu"),
    @SerialName("wasm") WASM("wasm"),
    @SerialName("auto") AUTO("auto");

    companion object {
        fun fromValue(value: String): ProcessorType =
            entries.find { it.value == value.lowercase() }
                ?: throw IllegalArgumentException("Unknown processor type: $value")
    }
}

/**
 * Precision levels for compute operations.
 */
@Serializable
enum class Precision(val value: String) {
    @SerialName("fp64") FP64("fp64"),
    @SerialName("fp32") FP32("fp32"),
    @SerialName("fp16") FP16("fp16"),
    @SerialName("bf16") BF16("bf16"),
    @SerialName("int8") INT8("int8"),
    @SerialName("int4") INT4("int4");

    companion object {
        fun fromValue(value: String): Precision =
            entries.find { it.value == value.lowercase() }
                ?: throw IllegalArgumentException("Unknown precision: $value")
    }
}

/**
 * Task priority levels.
 */
@Serializable
enum class Priority(val value: String) {
    @SerialName("critical") CRITICAL("critical"),
    @SerialName("high") HIGH("high"),
    @SerialName("normal") NORMAL("normal"),
    @SerialName("low") LOW("low"),
    @SerialName("background") BACKGROUND("background");
}

/**
 * Job execution status.
 */
@Serializable
enum class JobStatus(val value: String) {
    @SerialName("pending") PENDING("pending"),
    @SerialName("queued") QUEUED("queued"),
    @SerialName("running") RUNNING("running"),
    @SerialName("completed") COMPLETED("completed"),
    @SerialName("failed") FAILED("failed"),
    @SerialName("cancelled") CANCELLED("cancelled");
}

/**
 * Model categories.
 */
@Serializable
enum class ModelCategory(val value: String) {
    @SerialName("llm") LLM("llm"),
    @SerialName("embedding") EMBEDDING("embedding"),
    @SerialName("image_generation") IMAGE_GENERATION("image_generation"),
    @SerialName("image_classification") IMAGE_CLASSIFICATION("image_classification"),
    @SerialName("object_detection") OBJECT_DETECTION("object_detection"),
    @SerialName("speech_to_text") SPEECH_TO_TEXT("speech_to_text"),
    @SerialName("text_to_speech") TEXT_TO_SPEECH("text_to_speech"),
    @SerialName("code") CODE("code"),
    @SerialName("custom") CUSTOM("custom");
}

/**
 * SDK configuration.
 */
data class SynorConfig(
    val apiKey: String,
    val baseUrl: String = "https://api.synor.io/compute/v1",
    val defaultProcessor: ProcessorType = ProcessorType.AUTO,
    val defaultPrecision: Precision = Precision.FP32,
    val defaultPriority: Priority = Priority.NORMAL,
    val timeoutMs: Long = 30000,
    val debug: Boolean = false
)

/**
 * Matrix multiplication options.
 */
data class MatMulOptions(
    val precision: Precision = Precision.FP32,
    val processor: ProcessorType = ProcessorType.AUTO,
    val priority: Priority = Priority.NORMAL
)

/**
 * Convolution options.
 */
data class Conv2dOptions(
    val stride: Pair<Int, Int> = 1 to 1,
    val padding: Pair<Int, Int> = 0 to 0,
    val precision: Precision = Precision.FP32,
    val processor: ProcessorType = ProcessorType.AUTO
)

/**
 * Attention options.
 */
data class AttentionOptions(
    val numHeads: Int = 8,
    val flash: Boolean = true,
    val precision: Precision = Precision.FP16,
    val processor: ProcessorType = ProcessorType.GPU
)

/**
 * Inference options.
 */
data class InferenceOptions(
    val maxTokens: Int = 256,
    val temperature: Double = 0.7,
    val topP: Double = 0.9,
    val topK: Int = 50,
    val processor: ProcessorType? = null
)

/**
 * Job result.
 */
@Serializable
data class JobResult<T>(
    @SerialName("job_id") val jobId: String? = null,
    val status: JobStatus = JobStatus.PENDING,
    val result: T? = null,
    val error: String? = null,
    @SerialName("execution_time_ms") val executionTimeMs: Long? = null,
    val processor: ProcessorType? = null,
    val cost: Double? = null
) {
    val isSuccess: Boolean get() = status == JobStatus.COMPLETED && error == null
    val isFailed: Boolean get() = status == JobStatus.FAILED || error != null
}

/**
 * Model information.
 */
@Serializable
data class ModelInfo(
    val id: String,
    val name: String,
    val description: String? = null,
    val category: String,
    val parameters: Long? = null,
    @SerialName("context_length") val contextLength: Int? = null,
    val format: String? = null,
    @SerialName("recommended_processor") val recommendedProcessor: String? = null,
    val license: String? = null,
    val cid: String? = null
) {
    val formattedParameters: String get() = when {
        parameters == null -> "Unknown"
        parameters >= 1_000_000_000L -> "${parameters / 1_000_000_000}B"
        parameters >= 1_000_000L -> "${parameters / 1_000_000}M"
        parameters >= 1_000L -> "${parameters / 1_000}K"
        else -> parameters.toString()
    }
}

/**
 * Pricing information.
 */
@Serializable
data class PricingInfo(
    val processor: String,
    @SerialName("price_per_second") val pricePerSecond: Double,
    @SerialName("available_units") val availableUnits: Int,
    @SerialName("utilization_percent") val utilizationPercent: Double,
    @SerialName("aws_equivalent_price") val awsEquivalentPrice: Double? = null,
    @SerialName("savings_percent") val savingsPercent: Double? = null
)

/**
 * Usage statistics.
 */
@Serializable
data class UsageStats(
    @SerialName("total_jobs") val totalJobs: Int,
    @SerialName("completed_jobs") val completedJobs: Int,
    @SerialName("failed_jobs") val failedJobs: Int,
    @SerialName("total_compute_seconds") val totalComputeSeconds: Double,
    @SerialName("total_cost") val totalCost: Double
)
