use serde_json::{Value, json};

use crate::client::Client;
use crate::error::Error;
use crate::params::{
    BlobsParams, DecimalFractionsParams, GaussiansParams, IntegerSequencesMultiformParams,
    IntegerSequencesUniformParams, IntegersParams, SignedExtras, StringsParams, UuidsParams,
};
use crate::types::SignedResult;

fn merge_extras(params: &mut Value, extras: SignedExtras) {
    let obj = params.as_object_mut().expect("params is a JSON object");
    obj.insert("userData".into(), extras.user_data.unwrap_or(Value::Null));
    obj.insert(
        "ticketId".into(),
        extras.ticket_id.map(Value::String).unwrap_or(Value::Null),
    );
    obj.insert("licenseData".into(), extras.license.unwrap_or(Value::Null));
}

impl Client {
    pub async fn generate_signed_integers(
        &self,
        p: IntegersParams,
        extras: SignedExtras,
    ) -> Result<SignedResult, Error> {
        let mut params = json!({
            "n": p.n,
            "min": p.min,
            "max": p.max,
            "replacement": p.replacement,
            "base": p.base,
            "pregeneratedRandomization": p.pregenerated,
        });
        merge_extras(&mut params, extras);
        self.call_keyed("generateSignedIntegers", params).await
    }

    pub async fn generate_signed_integer_sequences_uniform(
        &self,
        p: IntegerSequencesUniformParams,
        extras: SignedExtras,
    ) -> Result<SignedResult, Error> {
        let mut params = json!({
            "n": p.n,
            "length": p.length,
            "min": p.min,
            "max": p.max,
            "replacement": p.replacement,
            "base": p.base,
            "pregeneratedRandomization": p.pregenerated,
        });
        merge_extras(&mut params, extras);
        self.call_keyed("generateSignedIntegerSequences", params)
            .await
    }

    pub async fn generate_signed_integer_sequences_multiform(
        &self,
        p: IntegerSequencesMultiformParams,
        extras: SignedExtras,
    ) -> Result<SignedResult, Error> {
        let mut params = json!({
            "n": p.n,
            "length": p.length,
            "min": p.min,
            "max": p.max,
            "replacement": p.replacement,
            "base": p.base,
            "pregeneratedRandomization": p.pregenerated,
        });
        merge_extras(&mut params, extras);
        self.call_keyed("generateSignedIntegerSequences", params)
            .await
    }

    pub async fn generate_signed_decimal_fractions(
        &self,
        p: DecimalFractionsParams,
        extras: SignedExtras,
    ) -> Result<SignedResult, Error> {
        let mut params = json!({
            "n": p.n,
            "decimalPlaces": p.decimal_places,
            "replacement": p.replacement,
            "pregeneratedRandomization": p.pregenerated,
        });
        merge_extras(&mut params, extras);
        self.call_keyed("generateSignedDecimalFractions", params)
            .await
    }

    pub async fn generate_signed_gaussians(
        &self,
        p: GaussiansParams,
        extras: SignedExtras,
    ) -> Result<SignedResult, Error> {
        let mut params = json!({
            "n": p.n,
            "mean": p.mean,
            "standardDeviation": p.std_dev,
            "significantDigits": p.significant_digits,
            "pregeneratedRandomization": p.pregenerated,
        });
        merge_extras(&mut params, extras);
        self.call_keyed("generateSignedGaussians", params).await
    }

    pub async fn generate_signed_strings(
        &self,
        p: StringsParams,
        extras: SignedExtras,
    ) -> Result<SignedResult, Error> {
        let mut params = json!({
            "n": p.n,
            "length": p.length,
            "characters": p.characters,
            "replacement": p.replacement,
            "pregeneratedRandomization": p.pregenerated,
        });
        merge_extras(&mut params, extras);
        self.call_keyed("generateSignedStrings", params).await
    }

    pub async fn generate_signed_uuids(
        &self,
        p: UuidsParams,
        extras: SignedExtras,
    ) -> Result<SignedResult, Error> {
        let mut params = json!({
            "n": p.n,
            "pregeneratedRandomization": p.pregenerated,
        });
        merge_extras(&mut params, extras);
        self.call_keyed("generateSignedUUIDs", params).await
    }

    pub async fn generate_signed_blobs(
        &self,
        p: BlobsParams,
        extras: SignedExtras,
    ) -> Result<SignedResult, Error> {
        let mut params = json!({
            "n": p.n,
            "size": p.size,
            "format": p.format,
            "pregeneratedRandomization": p.pregenerated,
        });
        merge_extras(&mut params, extras);
        self.call_keyed("generateSignedBlobs", params).await
    }
}
