# frozen_string_literal: true

module SynorCompute
  # Supported processor types
  module ProcessorType
    CPU = :cpu
    GPU = :gpu
    TPU = :tpu
    NPU = :npu
    LPU = :lpu
    FPGA = :fpga
    DSP = :dsp
    WEBGPU = :webgpu
    WASM = :wasm
    AUTO = :auto

    ALL = [CPU, GPU, TPU, NPU, LPU, FPGA, DSP, WEBGPU, WASM, AUTO].freeze
  end

  # Precision levels
  module Precision
    FP64 = :fp64
    FP32 = :fp32
    FP16 = :fp16
    BF16 = :bf16
    INT8 = :int8
    INT4 = :int4

    ALL = [FP64, FP32, FP16, BF16, INT8, INT4].freeze
  end

  # Task priority
  module Priority
    CRITICAL = :critical
    HIGH = :high
    NORMAL = :normal
    LOW = :low
    BACKGROUND = :background

    ALL = [CRITICAL, HIGH, NORMAL, LOW, BACKGROUND].freeze
  end

  # Job status
  module JobStatus
    PENDING = :pending
    QUEUED = :queued
    RUNNING = :running
    COMPLETED = :completed
    FAILED = :failed
    CANCELLED = :cancelled

    ALL = [PENDING, QUEUED, RUNNING, COMPLETED, FAILED, CANCELLED].freeze
  end

  # Model categories
  module ModelCategory
    LLM = :llm
    EMBEDDING = :embedding
    IMAGE_GENERATION = :image_generation
    IMAGE_CLASSIFICATION = :image_classification
    OBJECT_DETECTION = :object_detection
    SPEECH_TO_TEXT = :speech_to_text
    TEXT_TO_SPEECH = :text_to_speech
    CODE = :code
    CUSTOM = :custom

    ALL = [LLM, EMBEDDING, IMAGE_GENERATION, IMAGE_CLASSIFICATION,
           OBJECT_DETECTION, SPEECH_TO_TEXT, TEXT_TO_SPEECH, CODE, CUSTOM].freeze
  end

  # Configuration
  Config = Struct.new(
    :api_key,
    :base_url,
    :default_processor,
    :default_precision,
    :default_priority,
    :timeout,
    :debug,
    keyword_init: true
  ) do
    def initialize(api_key:, base_url: "https://api.synor.io/compute/v1",
                   default_processor: ProcessorType::AUTO,
                   default_precision: Precision::FP32,
                   default_priority: Priority::NORMAL,
                   timeout: 30, debug: false)
      super
    end
  end

  # Matrix multiplication options
  MatMulOptions = Struct.new(:precision, :processor, :priority, keyword_init: true) do
    def initialize(precision: Precision::FP32, processor: ProcessorType::AUTO,
                   priority: Priority::NORMAL)
      super
    end
  end

  # Convolution options
  Conv2dOptions = Struct.new(:stride, :padding, :precision, :processor, keyword_init: true) do
    def initialize(stride: [1, 1], padding: [0, 0], precision: Precision::FP32,
                   processor: ProcessorType::AUTO)
      super
    end
  end

  # Attention options
  AttentionOptions = Struct.new(:num_heads, :flash, :precision, :processor, keyword_init: true) do
    def initialize(num_heads: 8, flash: true, precision: Precision::FP16,
                   processor: ProcessorType::GPU)
      super
    end
  end

  # Inference options
  InferenceOptions = Struct.new(:max_tokens, :temperature, :top_p, :top_k, :processor,
                                keyword_init: true) do
    def initialize(max_tokens: 256, temperature: 0.7, top_p: 0.9, top_k: 50, processor: nil)
      super
    end
  end

  # Job result
  class JobResult
    attr_reader :job_id, :status, :result, :error, :execution_time_ms, :processor, :cost

    def initialize(job_id: nil, status: JobStatus::PENDING, result: nil, error: nil,
                   execution_time_ms: nil, processor: nil, cost: nil)
      @job_id = job_id
      @status = status.is_a?(Symbol) ? status : status&.to_sym
      @result = result
      @error = error
      @execution_time_ms = execution_time_ms
      @processor = processor.is_a?(Symbol) ? processor : processor&.to_sym
      @cost = cost
    end

    def success?
      @status == JobStatus::COMPLETED && @error.nil?
    end

    def failed?
      @status == JobStatus::FAILED || !@error.nil?
    end

    def self.from_hash(hash)
      new(
        job_id: hash["job_id"],
        status: hash["status"],
        result: hash["result"],
        error: hash["error"],
        execution_time_ms: hash["execution_time_ms"],
        processor: hash["processor"],
        cost: hash["cost"]
      )
    end
  end

  # Model info
  class ModelInfo
    attr_reader :id, :name, :description, :category, :parameters, :context_length,
                :format, :recommended_processor, :license, :cid

    def initialize(id:, name:, description: nil, category:, parameters: nil,
                   context_length: nil, format: nil, recommended_processor: nil,
                   license: nil, cid: nil)
      @id = id
      @name = name
      @description = description
      @category = category
      @parameters = parameters
      @context_length = context_length
      @format = format
      @recommended_processor = recommended_processor
      @license = license
      @cid = cid
    end

    def formatted_parameters
      return "Unknown" unless @parameters

      if @parameters >= 1_000_000_000
        "#{@parameters / 1_000_000_000}B"
      elsif @parameters >= 1_000_000
        "#{@parameters / 1_000_000}M"
      elsif @parameters >= 1_000
        "#{@parameters / 1_000}K"
      else
        @parameters.to_s
      end
    end

    def self.from_hash(hash)
      new(
        id: hash["id"],
        name: hash["name"],
        description: hash["description"],
        category: hash["category"],
        parameters: hash["parameters"],
        context_length: hash["context_length"],
        format: hash["format"],
        recommended_processor: hash["recommended_processor"],
        license: hash["license"],
        cid: hash["cid"]
      )
    end
  end

  # Pricing info
  class PricingInfo
    attr_reader :processor, :price_per_second, :available_units, :utilization_percent,
                :aws_equivalent_price, :savings_percent

    def initialize(processor:, price_per_second:, available_units:, utilization_percent:,
                   aws_equivalent_price: nil, savings_percent: nil)
      @processor = processor
      @price_per_second = price_per_second
      @available_units = available_units
      @utilization_percent = utilization_percent
      @aws_equivalent_price = aws_equivalent_price
      @savings_percent = savings_percent
    end

    def self.from_hash(hash)
      new(
        processor: hash["processor"],
        price_per_second: hash["price_per_second"],
        available_units: hash["available_units"],
        utilization_percent: hash["utilization_percent"],
        aws_equivalent_price: hash["aws_equivalent_price"],
        savings_percent: hash["savings_percent"]
      )
    end
  end

  # Usage stats
  class UsageStats
    attr_reader :total_jobs, :completed_jobs, :failed_jobs, :total_compute_seconds, :total_cost

    def initialize(total_jobs:, completed_jobs:, failed_jobs:, total_compute_seconds:, total_cost:)
      @total_jobs = total_jobs
      @completed_jobs = completed_jobs
      @failed_jobs = failed_jobs
      @total_compute_seconds = total_compute_seconds
      @total_cost = total_cost
    end

    def self.from_hash(hash)
      new(
        total_jobs: hash["total_jobs"],
        completed_jobs: hash["completed_jobs"],
        failed_jobs: hash["failed_jobs"],
        total_compute_seconds: hash["total_compute_seconds"],
        total_cost: hash["total_cost"]
      )
    end
  end
end
