//! Type definitions for the Synor Compute SDK.

use serde::{Deserialize, Serialize};

/// Supported processor types for heterogeneous computing.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum ProcessorType {
    Cpu,
    Gpu,
    Tpu,
    Npu,
    Lpu,
    Fpga,
    Dsp,
    WebGpu,
    Wasm,
    #[default]
    Auto,
}

impl ProcessorType {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Cpu => "cpu",
            Self::Gpu => "gpu",
            Self::Tpu => "tpu",
            Self::Npu => "npu",
            Self::Lpu => "lpu",
            Self::Fpga => "fpga",
            Self::Dsp => "dsp",
            Self::WebGpu => "webgpu",
            Self::Wasm => "wasm",
            Self::Auto => "auto",
        }
    }
}

/// Precision levels for compute operations.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum Precision {
    FP64,
    #[default]
    FP32,
    FP16,
    BF16,
    INT8,
    INT4,
}

impl Precision {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::FP64 => "fp64",
            Self::FP32 => "fp32",
            Self::FP16 => "fp16",
            Self::BF16 => "bf16",
            Self::INT8 => "int8",
            Self::INT4 => "int4",
        }
    }
}

/// Task priority levels.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum Priority {
    Critical,
    High,
    #[default]
    Normal,
    Low,
    Background,
}

/// Job execution status.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum JobStatus {
    #[default]
    Pending,
    Queued,
    Running,
    Completed,
    Failed,
    Cancelled,
}

/// Model categories.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ModelCategory {
    Llm,
    Embedding,
    ImageGeneration,
    ImageClassification,
    ObjectDetection,
    SpeechToText,
    TextToSpeech,
    Code,
    Custom,
}

/// SDK configuration.
#[derive(Debug, Clone)]
pub struct Config {
    pub api_key: String,
    pub base_url: String,
    pub default_processor: ProcessorType,
    pub default_precision: Precision,
    pub default_priority: Priority,
    pub timeout_secs: u64,
    pub debug: bool,
}

impl Config {
    pub fn new(api_key: impl Into<String>) -> Self {
        Self {
            api_key: api_key.into(),
            base_url: "https://api.synor.io/compute/v1".to_string(),
            default_processor: ProcessorType::Auto,
            default_precision: Precision::FP32,
            default_priority: Priority::Normal,
            timeout_secs: 30,
            debug: false,
        }
    }

    pub fn base_url(mut self, url: impl Into<String>) -> Self {
        self.base_url = url.into();
        self
    }

    pub fn default_processor(mut self, processor: ProcessorType) -> Self {
        self.default_processor = processor;
        self
    }

    pub fn default_precision(mut self, precision: Precision) -> Self {
        self.default_precision = precision;
        self
    }

    pub fn timeout_secs(mut self, secs: u64) -> Self {
        self.timeout_secs = secs;
        self
    }

    pub fn debug(mut self, debug: bool) -> Self {
        self.debug = debug;
        self
    }
}

/// Matrix multiplication options.
#[derive(Debug, Clone, Default)]
pub struct MatMulOptions {
    pub precision: Precision,
    pub processor: ProcessorType,
    pub priority: Priority,
}

/// Convolution options.
#[derive(Debug, Clone)]
pub struct Conv2dOptions {
    pub stride: (usize, usize),
    pub padding: (usize, usize),
    pub precision: Precision,
    pub processor: ProcessorType,
}

impl Default for Conv2dOptions {
    fn default() -> Self {
        Self {
            stride: (1, 1),
            padding: (0, 0),
            precision: Precision::FP32,
            processor: ProcessorType::Auto,
        }
    }
}

/// Attention options.
#[derive(Debug, Clone)]
pub struct AttentionOptions {
    pub num_heads: usize,
    pub flash: bool,
    pub precision: Precision,
    pub processor: ProcessorType,
}

impl Default for AttentionOptions {
    fn default() -> Self {
        Self {
            num_heads: 8,
            flash: true,
            precision: Precision::FP16,
            processor: ProcessorType::GPU,
        }
    }
}

/// Inference options.
#[derive(Debug, Clone)]
pub struct InferenceOptions {
    pub max_tokens: usize,
    pub temperature: f64,
    pub top_p: f64,
    pub top_k: usize,
    pub processor: Option<ProcessorType>,
}

impl Default for InferenceOptions {
    fn default() -> Self {
        Self {
            max_tokens: 256,
            temperature: 0.7,
            top_p: 0.9,
            top_k: 50,
            processor: None,
        }
    }
}

/// Job result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobResult<T> {
    #[serde(default)]
    pub job_id: Option<String>,
    #[serde(default)]
    pub status: JobStatus,
    #[serde(default)]
    pub result: Option<T>,
    #[serde(default)]
    pub error: Option<String>,
    #[serde(default)]
    pub execution_time_ms: Option<i64>,
    #[serde(default)]
    pub processor: Option<ProcessorType>,
    #[serde(default)]
    pub cost: Option<f64>,
}

impl<T> JobResult<T> {
    pub fn is_success(&self) -> bool {
        self.status == JobStatus::Completed && self.error.is_none()
    }

    pub fn is_failed(&self) -> bool {
        self.status == JobStatus::Failed || self.error.is_some()
    }
}

/// Model information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelInfo {
    pub id: String,
    pub name: String,
    #[serde(default)]
    pub description: Option<String>,
    pub category: String,
    #[serde(default)]
    pub parameters: Option<i64>,
    #[serde(default)]
    pub context_length: Option<i32>,
    #[serde(default)]
    pub format: Option<String>,
    #[serde(default)]
    pub recommended_processor: Option<String>,
    #[serde(default)]
    pub license: Option<String>,
    #[serde(default)]
    pub cid: Option<String>,
}

impl ModelInfo {
    pub fn formatted_parameters(&self) -> String {
        match self.parameters {
            None => "Unknown".to_string(),
            Some(p) if p >= 1_000_000_000 => format!("{}B", p / 1_000_000_000),
            Some(p) if p >= 1_000_000 => format!("{}M", p / 1_000_000),
            Some(p) if p >= 1_000 => format!("{}K", p / 1_000),
            Some(p) => p.to_string(),
        }
    }
}

/// Pricing information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PricingInfo {
    pub processor: String,
    pub price_per_second: f64,
    pub available_units: i32,
    pub utilization_percent: f64,
    #[serde(default)]
    pub aws_equivalent_price: Option<f64>,
    #[serde(default)]
    pub savings_percent: Option<f64>,
}

/// Usage statistics.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsageStats {
    pub total_jobs: i32,
    pub completed_jobs: i32,
    pub failed_jobs: i32,
    pub total_compute_seconds: f64,
    pub total_cost: f64,
}
