/**
 * Synor Compute SDK - C++ Client
 *
 * Modern C++20 SDK for distributed heterogeneous computing.
 *
 * @example
 * ```cpp
 * #include <synor/compute.hpp>
 *
 * int main() {
 *     using namespace synor;
 *
 *     // Create client
 *     auto client = SynorCompute("your-api-key");
 *
 *     // Matrix multiplication on GPU
 *     auto a = Tensor::rand({512, 512});
 *     auto b = Tensor::rand({512, 512});
 *     auto result = client.matmul(a, b, {
 *         .precision = Precision::FP16,
 *         .processor = ProcessorType::GPU
 *     });
 *
 *     if (result.is_success()) {
 *         std::cout << "Time: " << result.execution_time_ms << "ms\n";
 *     }
 *
 *     // LLM inference
 *     auto response = client.inference("llama-3-70b", "Explain quantum computing");
 *     std::cout << response.result << "\n";
 *
 *     // Streaming inference
 *     client.inference_stream("llama-3-70b", "Write a poem", [](const std::string& token) {
 *         std::cout << token << std::flush;
 *     });
 *
 *     return 0;
 * }
 * ```
 */

#pragma once

#include <string>
#include <vector>
#include <optional>
#include <functional>
#include <memory>
#include <span>
#include <expected>
#include <cstdint>

namespace synor {

constexpr const char* VERSION = "0.1.0";

// ============ Enums ============

enum class ProcessorType {
    CPU,
    GPU,
    TPU,
    NPU,
    LPU,
    FPGA,
    DSP,
    WebGPU,
    WASM,
    Auto
};

enum class Precision {
    FP64,
    FP32,
    FP16,
    BF16,
    INT8,
    INT4
};

enum class Priority {
    Critical,
    High,
    Normal,
    Low,
    Background
};

enum class JobStatus {
    Pending,
    Queued,
    Running,
    Completed,
    Failed,
    Cancelled
};

enum class ModelCategory {
    LLM,
    Embedding,
    ImageGeneration,
    ImageClassification,
    ObjectDetection,
    SpeechToText,
    TextToSpeech,
    Code,
    Custom
};

// ============ Error Handling ============

enum class ErrorCode {
    None,
    InvalidArgument,
    OutOfMemory,
    Network,
    Api,
    Timeout,
    ClientClosed
};

struct Error {
    ErrorCode code;
    std::string message;

    Error() : code(ErrorCode::None) {}
    Error(ErrorCode c, std::string msg) : code(c), message(std::move(msg)) {}

    explicit operator bool() const { return code != ErrorCode::None; }
};

template<typename T>
using Result = std::expected<T, Error>;

// ============ Configuration ============

struct Config {
    std::string api_key;
    std::string base_url = "https://api.synor.io/compute/v1";
    ProcessorType default_processor = ProcessorType::Auto;
    Precision default_precision = Precision::FP32;
    Priority default_priority = Priority::Normal;
    int timeout_ms = 30000;
    bool debug = false;
};

// ============ Options ============

struct MatMulOptions {
    Precision precision = Precision::FP32;
    ProcessorType processor = ProcessorType::Auto;
    Priority priority = Priority::Normal;
};

struct Conv2dOptions {
    std::pair<int, int> stride = {1, 1};
    std::pair<int, int> padding = {0, 0};
    Precision precision = Precision::FP32;
    ProcessorType processor = ProcessorType::Auto;
};

struct AttentionOptions {
    int num_heads = 8;
    bool flash = true;
    Precision precision = Precision::FP16;
    ProcessorType processor = ProcessorType::GPU;
};

struct InferenceOptions {
    int max_tokens = 256;
    double temperature = 0.7;
    double top_p = 0.9;
    int top_k = 50;
    std::optional<ProcessorType> processor;
};

// ============ Tensor ============

class Tensor {
public:
    Tensor(std::vector<int> shape, std::vector<double> data, Precision dtype = Precision::FP32);
    Tensor(std::vector<int> shape, std::span<const double> data, Precision dtype = Precision::FP32);

    // Factory methods
    static Tensor zeros(std::vector<int> shape, Precision dtype = Precision::FP32);
    static Tensor ones(std::vector<int> shape, Precision dtype = Precision::FP32);
    static Tensor rand(std::vector<int> shape, Precision dtype = Precision::FP32);
    static Tensor randn(std::vector<int> shape, Precision dtype = Precision::FP32);
    static Tensor eye(int n, Precision dtype = Precision::FP32);
    static Tensor arange(double start, double end, double step = 1.0);
    static Tensor linspace(double start, double end, int num);

    // Properties
    [[nodiscard]] const std::vector<int>& shape() const { return shape_; }
    [[nodiscard]] const std::vector<double>& data() const { return data_; }
    [[nodiscard]] Precision dtype() const { return dtype_; }
    [[nodiscard]] size_t size() const { return data_.size(); }
    [[nodiscard]] int ndim() const { return static_cast<int>(shape_.size()); }

    // Operations
    [[nodiscard]] Tensor reshape(std::vector<int> new_shape) const;
    [[nodiscard]] Tensor transpose() const;

    // Reductions
    [[nodiscard]] double mean() const;
    [[nodiscard]] double sum() const;
    [[nodiscard]] double std() const;
    [[nodiscard]] double max() const;
    [[nodiscard]] double min() const;

    // Activations
    [[nodiscard]] Tensor relu() const;
    [[nodiscard]] Tensor sigmoid() const;
    [[nodiscard]] Tensor softmax() const;

    // Element access
    [[nodiscard]] double operator()(std::initializer_list<int> indices) const;

    // Equality
    bool operator==(const Tensor& other) const;

private:
    std::vector<int> shape_;
    std::vector<double> data_;
    Precision dtype_;
};

// ============ Results ============

template<typename T>
struct JobResult {
    std::optional<std::string> job_id;
    JobStatus status = JobStatus::Pending;
    std::optional<T> result;
    std::optional<std::string> error;
    std::optional<int64_t> execution_time_ms;
    std::optional<ProcessorType> processor;
    std::optional<double> cost;

    [[nodiscard]] bool is_success() const {
        return status == JobStatus::Completed && !error.has_value();
    }

    [[nodiscard]] bool is_failed() const {
        return status == JobStatus::Failed || error.has_value();
    }
};

struct ModelInfo {
    std::string id;
    std::string name;
    std::optional<std::string> description;
    ModelCategory category;
    std::optional<int64_t> parameters;
    std::optional<int> context_length;
    std::optional<std::string> format;
    std::optional<ProcessorType> recommended_processor;
    std::optional<std::string> license;
    std::optional<std::string> cid;

    [[nodiscard]] std::string formatted_parameters() const;
};

struct PricingInfo {
    ProcessorType processor;
    double price_per_second;
    int available_units;
    double utilization_percent;
    std::optional<double> aws_equivalent_price;
    std::optional<double> savings_percent;
};

struct UsageStats {
    int total_jobs;
    int completed_jobs;
    int failed_jobs;
    double total_compute_seconds;
    double total_cost;
};

// ============ Client ============

class SynorCompute {
public:
    explicit SynorCompute(const std::string& api_key);
    explicit SynorCompute(Config config);
    ~SynorCompute();

    // Non-copyable, movable
    SynorCompute(const SynorCompute&) = delete;
    SynorCompute& operator=(const SynorCompute&) = delete;
    SynorCompute(SynorCompute&&) noexcept;
    SynorCompute& operator=(SynorCompute&&) noexcept;

    // Matrix Operations
    [[nodiscard]] Result<JobResult<Tensor>> matmul(
        const Tensor& a,
        const Tensor& b,
        const MatMulOptions& options = {}
    );

    [[nodiscard]] Result<JobResult<Tensor>> conv2d(
        const Tensor& input,
        const Tensor& kernel,
        const Conv2dOptions& options = {}
    );

    [[nodiscard]] Result<JobResult<Tensor>> attention(
        const Tensor& query,
        const Tensor& key,
        const Tensor& value,
        const AttentionOptions& options = {}
    );

    // LLM Inference
    [[nodiscard]] Result<JobResult<std::string>> inference(
        const std::string& model,
        const std::string& prompt,
        const InferenceOptions& options = {}
    );

    Result<void> inference_stream(
        const std::string& model,
        const std::string& prompt,
        std::function<void(const std::string&)> on_token,
        const InferenceOptions& options = {}
    );

    // Model Registry
    [[nodiscard]] Result<std::vector<ModelInfo>> list_models(
        std::optional<ModelCategory> category = std::nullopt
    );

    [[nodiscard]] Result<ModelInfo> get_model(const std::string& model_id);

    [[nodiscard]] Result<std::vector<ModelInfo>> search_models(const std::string& query);

    // Pricing & Usage
    [[nodiscard]] Result<std::vector<PricingInfo>> get_pricing();
    [[nodiscard]] Result<UsageStats> get_usage();

    // Health Check
    [[nodiscard]] bool health_check();

    // Lifecycle
    void close();

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

// ============ Utility Functions ============

[[nodiscard]] std::string_view to_string(ProcessorType type);
[[nodiscard]] std::string_view to_string(Precision precision);
[[nodiscard]] std::string_view to_string(Priority priority);
[[nodiscard]] std::string_view to_string(JobStatus status);
[[nodiscard]] std::string_view to_string(ModelCategory category);

[[nodiscard]] std::optional<ProcessorType> processor_from_string(std::string_view str);
[[nodiscard]] std::optional<Precision> precision_from_string(std::string_view str);

} // namespace synor
