use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Call {
    pub method: String,
    pub params: serde_json::Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Node {
    pub name: String,
    pub url: String,
    pub remote: Option<String>,
    pub client_version: Option<String>,
    pub network: Option<String>,
}

pub type Nodes = HashMap<String, Node>;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VegetaAttack {
    pub rate: u64,
    pub duration: u64,
    pub calls: Vec<serde_json::Value>,
    pub vegeta_args: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestGenerationParameters {
    pub aqua_version: String,
    pub test_name: String,
    pub random_seed: Option<u64>,
    pub rates: Option<Vec<u64>>,
    pub durations: Option<Vec<u64>>,
    pub vegeta_args: Option<String>,
    pub network: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoadTest {
    pub test_parameters: TestGenerationParameters,
    pub attacks: Vec<VegetaAttack>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LoadTestOutputDatum {
    pub target_rate: u64,
    pub actual_rate: Option<f64>,
    pub target_duration: u64,
    pub actual_duration: Option<f64>,
    pub requests: u64,
    pub throughput: Option<f64>,
    pub success: Option<f64>,
    pub min: Option<f64>,
    pub mean: Option<f64>,
    pub p50: Option<f64>,
    pub p90: Option<f64>,
    pub p95: Option<f64>,
    pub p99: Option<f64>,
    pub max: Option<f64>,
    pub status_codes: HashMap<String, u64>,
    pub errors: Vec<String>,
    pub first_request_timestamp: Option<String>,
    pub last_request_timestamp: Option<String>,
    pub last_response_timestamp: Option<String>,
    pub final_wait_time: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LoadTestOutput {
    pub target_rate: Vec<u64>,
    pub actual_rate: Vec<Option<f64>>,
    pub target_duration: Vec<u64>,
    pub actual_duration: Vec<Option<f64>>,
    pub requests: Vec<u64>,
    pub throughput: Vec<Option<f64>>,
    pub success: Vec<Option<f64>>,
    pub min: Vec<Option<f64>>,
    pub mean: Vec<Option<f64>>,
    pub p50: Vec<Option<f64>>,
    pub p90: Vec<Option<f64>>,
    pub p95: Vec<Option<f64>>,
    pub p99: Vec<Option<f64>>,
    pub max: Vec<Option<f64>>,
    pub status_codes: Vec<HashMap<String, u64>>,
    pub errors: Vec<Vec<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SingleRunTestPayload {
    pub aqua_version: String,
    pub run_type: String,
    pub name: String,
    pub test_parameters: TestGenerationParameters,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SingleRunResultsPayload {
    pub aqua_version: String,
    pub cli_args: Vec<String>,
    pub run_type: String,
    pub t_run_start: f64,
    pub t_run_end: f64,
    pub nodes: Nodes,
    pub results: HashMap<String, LoadTestOutput>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RunOutput {
    pub output_dir: String,
    pub test: Option<LoadTest>,
    pub test_parameters: TestGenerationParameters,
    pub payload: SingleRunResultsPayload,
}

#[derive(Debug, thiserror::Error)]
pub enum AquaError {
    #[error("RPC error: {0}")]
    Rpc(String),
    #[error("Invalid test: {0}")]
    InvalidTest(String),
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),
    #[error("Request error: {0}")]
    Request(#[from] reqwest::Error),
}
