//! Parameter structs for the RANDOM.ORG generation methods.
//!
//! Each method has a corresponding builder with required fields in `new(...)`
//! and chainable setters for optional ones.

use serde_json::Value;

use crate::types::{BlobFormat, BoolOrArray, IntOrArray, Pregenerated};

macro_rules! setter {
    (req $field:ident: $ty:ty) => {
        pub fn $field(mut self, v: $ty) -> Self {
            self.$field = v;
            self
        }
    };
    (opt $field:ident: $ty:ty) => {
        pub fn $field(mut self, v: $ty) -> Self {
            self.$field = Some(v);
            self
        }
    };
    (opt_into $field:ident: $ty:ty) => {
        pub fn $field(mut self, v: impl Into<$ty>) -> Self {
            self.$field = Some(v.into());
            self
        }
    };
}

#[derive(Debug, Clone)]
pub struct IntegersParams {
    pub n: u32,
    pub min: i64,
    pub max: i64,
    pub replacement: bool,
    pub base: u8,
    pub pregenerated: Option<Pregenerated>,
}

impl IntegersParams {
    pub fn new(n: u32, min: i64, max: i64) -> Self {
        Self {
            n,
            min,
            max,
            replacement: true,
            base: 10,
            pregenerated: None,
        }
    }
    setter!(req replacement: bool);
    setter!(req base: u8);
    setter!(opt pregenerated: Pregenerated);
}

#[derive(Debug, Clone)]
pub struct IntegerSequencesUniformParams {
    pub n: u32,
    pub length: u32,
    pub min: i64,
    pub max: i64,
    pub replacement: bool,
    pub base: u8,
    pub pregenerated: Option<Pregenerated>,
}

impl IntegerSequencesUniformParams {
    pub fn new(n: u32, length: u32, min: i64, max: i64) -> Self {
        Self {
            n,
            length,
            min,
            max,
            replacement: true,
            base: 10,
            pregenerated: None,
        }
    }
    setter!(req replacement: bool);
    setter!(req base: u8);
    setter!(opt pregenerated: Pregenerated);
}

#[derive(Debug, Clone)]
pub struct IntegerSequencesMultiformParams {
    pub n: u32,
    pub length: Vec<u32>,
    pub min: IntOrArray,
    pub max: IntOrArray,
    pub replacement: BoolOrArray,
    pub base: IntOrArray,
    pub pregenerated: Option<Pregenerated>,
}

impl IntegerSequencesMultiformParams {
    pub fn new(n: u32, length: Vec<u32>, min: IntOrArray, max: IntOrArray) -> Self {
        Self {
            n,
            length,
            min,
            max,
            replacement: BoolOrArray::One(true),
            base: IntOrArray::One(10),
            pregenerated: None,
        }
    }
    setter!(req replacement: BoolOrArray);
    setter!(req base: IntOrArray);
    setter!(opt pregenerated: Pregenerated);
}

#[derive(Debug, Clone)]
pub struct DecimalFractionsParams {
    pub n: u32,
    pub decimal_places: u8,
    pub replacement: bool,
    pub pregenerated: Option<Pregenerated>,
}

impl DecimalFractionsParams {
    pub fn new(n: u32, decimal_places: u8) -> Self {
        Self {
            n,
            decimal_places,
            replacement: true,
            pregenerated: None,
        }
    }
    setter!(req replacement: bool);
    setter!(opt pregenerated: Pregenerated);
}

#[derive(Debug, Clone)]
pub struct GaussiansParams {
    pub n: u32,
    pub mean: f64,
    pub std_dev: f64,
    pub significant_digits: u8,
    pub pregenerated: Option<Pregenerated>,
}

impl GaussiansParams {
    pub fn new(n: u32, mean: f64, std_dev: f64, significant_digits: u8) -> Self {
        Self {
            n,
            mean,
            std_dev,
            significant_digits,
            pregenerated: None,
        }
    }
    setter!(opt pregenerated: Pregenerated);
}

#[derive(Debug, Clone)]
pub struct StringsParams {
    pub n: u32,
    pub length: u32,
    pub characters: String,
    pub replacement: bool,
    pub pregenerated: Option<Pregenerated>,
}

impl StringsParams {
    pub fn new(n: u32, length: u32, characters: impl Into<String>) -> Self {
        Self {
            n,
            length,
            characters: characters.into(),
            replacement: true,
            pregenerated: None,
        }
    }
    setter!(req replacement: bool);
    setter!(opt pregenerated: Pregenerated);
}

#[derive(Debug, Clone)]
pub struct UuidsParams {
    pub n: u32,
    pub pregenerated: Option<Pregenerated>,
}

impl UuidsParams {
    pub fn new(n: u32) -> Self {
        Self {
            n,
            pregenerated: None,
        }
    }
    setter!(opt pregenerated: Pregenerated);
}

#[derive(Debug, Clone)]
pub struct BlobsParams {
    pub n: u32,
    pub size: u32,
    pub format: BlobFormat,
    pub pregenerated: Option<Pregenerated>,
}

impl BlobsParams {
    pub fn new(n: u32, size: u32) -> Self {
        Self {
            n,
            size,
            format: BlobFormat::Base64,
            pregenerated: None,
        }
    }
    setter!(req format: BlobFormat);
    setter!(opt pregenerated: Pregenerated);
}

/// Optional fields for any signed-API call: `userData`, `ticketId`, `licenseData`.
#[derive(Debug, Default, Clone)]
pub struct SignedExtras {
    pub user_data: Option<Value>,
    pub ticket_id: Option<String>,
    pub license: Option<Value>,
}

impl SignedExtras {
    pub fn new() -> Self {
        Self::default()
    }
    setter!(opt user_data: Value);
    setter!(opt_into ticket_id: String);
    setter!(opt license: Value);
}
