using System.Text.Json.Serialization;

namespace SynorCompute;

/// <summary>
/// Supported processor types for heterogeneous computing.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ProcessorType
{
    [JsonPropertyName("cpu")] Cpu,
    [JsonPropertyName("gpu")] Gpu,
    [JsonPropertyName("tpu")] Tpu,
    [JsonPropertyName("npu")] Npu,
    [JsonPropertyName("lpu")] Lpu,
    [JsonPropertyName("fpga")] Fpga,
    [JsonPropertyName("dsp")] Dsp,
    [JsonPropertyName("webgpu")] WebGpu,
    [JsonPropertyName("wasm")] Wasm,
    [JsonPropertyName("auto")] Auto
}

/// <summary>
/// Precision levels for compute operations.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Precision
{
    [JsonPropertyName("fp64")] Fp64,
    [JsonPropertyName("fp32")] Fp32,
    [JsonPropertyName("fp16")] Fp16,
    [JsonPropertyName("bf16")] Bf16,
    [JsonPropertyName("int8")] Int8,
    [JsonPropertyName("int4")] Int4
}

/// <summary>
/// Task priority levels.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Priority
{
    [JsonPropertyName("critical")] Critical,
    [JsonPropertyName("high")] High,
    [JsonPropertyName("normal")] Normal,
    [JsonPropertyName("low")] Low,
    [JsonPropertyName("background")] Background
}

/// <summary>
/// Job execution status.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum JobStatus
{
    [JsonPropertyName("pending")] Pending,
    [JsonPropertyName("queued")] Queued,
    [JsonPropertyName("running")] Running,
    [JsonPropertyName("completed")] Completed,
    [JsonPropertyName("failed")] Failed,
    [JsonPropertyName("cancelled")] Cancelled
}

/// <summary>
/// Model categories.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ModelCategory
{
    [JsonPropertyName("llm")] Llm,
    [JsonPropertyName("embedding")] Embedding,
    [JsonPropertyName("image_generation")] ImageGeneration,
    [JsonPropertyName("image_classification")] ImageClassification,
    [JsonPropertyName("object_detection")] ObjectDetection,
    [JsonPropertyName("speech_to_text")] SpeechToText,
    [JsonPropertyName("text_to_speech")] TextToSpeech,
    [JsonPropertyName("code")] Code,
    [JsonPropertyName("custom")] Custom
}

/// <summary>
/// SDK configuration.
/// </summary>
public record SynorConfig
{
    public required string ApiKey { get; init; }
    public string BaseUrl { get; init; } = "https://api.synor.io/compute/v1";
    public ProcessorType DefaultProcessor { get; init; } = ProcessorType.Auto;
    public Precision DefaultPrecision { get; init; } = Precision.Fp32;
    public Priority DefaultPriority { get; init; } = Priority.Normal;
    public int TimeoutMs { get; init; } = 30000;
    public bool Debug { get; init; } = false;
}

/// <summary>
/// Matrix multiplication options.
/// </summary>
public record MatMulOptions
{
    public Precision Precision { get; init; } = Precision.Fp32;
    public ProcessorType Processor { get; init; } = ProcessorType.Auto;
    public Priority Priority { get; init; } = Priority.Normal;
}

/// <summary>
/// Convolution options.
/// </summary>
public record Conv2dOptions
{
    public (int, int) Stride { get; init; } = (1, 1);
    public (int, int) Padding { get; init; } = (0, 0);
    public Precision Precision { get; init; } = Precision.Fp32;
    public ProcessorType Processor { get; init; } = ProcessorType.Auto;
}

/// <summary>
/// Attention options.
/// </summary>
public record AttentionOptions
{
    public int NumHeads { get; init; } = 8;
    public bool Flash { get; init; } = true;
    public Precision Precision { get; init; } = Precision.Fp16;
    public ProcessorType Processor { get; init; } = ProcessorType.Gpu;
}

/// <summary>
/// Inference options.
/// </summary>
public record InferenceOptions
{
    public int MaxTokens { get; init; } = 256;
    public double Temperature { get; init; } = 0.7;
    public double TopP { get; init; } = 0.9;
    public int TopK { get; init; } = 50;
    public ProcessorType? Processor { get; init; }
}

/// <summary>
/// Job result.
/// </summary>
public record JobResult<T>
{
    [JsonPropertyName("job_id")]
    public string? JobId { get; init; }

    public JobStatus Status { get; init; } = JobStatus.Pending;

    public T? Result { get; init; }

    public string? Error { get; init; }

    [JsonPropertyName("execution_time_ms")]
    public long? ExecutionTimeMs { get; init; }

    public ProcessorType? Processor { get; init; }

    public double? Cost { get; init; }

    public bool IsSuccess => Status == JobStatus.Completed && Error == null;
    public bool IsFailed => Status == JobStatus.Failed || Error != null;
}

/// <summary>
/// Model information.
/// </summary>
public record ModelInfo
{
    public required string Id { get; init; }
    public required string Name { get; init; }
    public string? Description { get; init; }
    public required string Category { get; init; }
    public long? Parameters { get; init; }

    [JsonPropertyName("context_length")]
    public int? ContextLength { get; init; }

    public string? Format { get; init; }

    [JsonPropertyName("recommended_processor")]
    public string? RecommendedProcessor { get; init; }

    public string? License { get; init; }
    public string? Cid { get; init; }

    public string FormattedParameters => Parameters switch
    {
        null => "Unknown",
        >= 1_000_000_000 => $"{Parameters / 1_000_000_000}B",
        >= 1_000_000 => $"{Parameters / 1_000_000}M",
        >= 1_000 => $"{Parameters / 1_000}K",
        _ => Parameters.ToString()!
    };
}

/// <summary>
/// Pricing information.
/// </summary>
public record PricingInfo
{
    public required string Processor { get; init; }

    [JsonPropertyName("price_per_second")]
    public double PricePerSecond { get; init; }

    [JsonPropertyName("available_units")]
    public int AvailableUnits { get; init; }

    [JsonPropertyName("utilization_percent")]
    public double UtilizationPercent { get; init; }

    [JsonPropertyName("aws_equivalent_price")]
    public double? AwsEquivalentPrice { get; init; }

    [JsonPropertyName("savings_percent")]
    public double? SavingsPercent { get; init; }
}

/// <summary>
/// Usage statistics.
/// </summary>
public record UsageStats
{
    [JsonPropertyName("total_jobs")]
    public int TotalJobs { get; init; }

    [JsonPropertyName("completed_jobs")]
    public int CompletedJobs { get; init; }

    [JsonPropertyName("failed_jobs")]
    public int FailedJobs { get; init; }

    [JsonPropertyName("total_compute_seconds")]
    public double TotalComputeSeconds { get; init; }

    [JsonPropertyName("total_cost")]
    public double TotalCost { get; init; }
}

/// <summary>
/// Synor Compute exception.
/// </summary>
public class SynorException : Exception
{
    public int? StatusCode { get; }

    public SynorException(string message, int? statusCode = null)
        : base(message)
    {
        StatusCode = statusCode;
    }
}
