use core::ptr;

use serde::Deserialize;

use crate::app_store::{AppStoreEnvironment, AppStorePlatform};
use crate::error::StoreKitError;
use crate::ffi;
use crate::private::{decode_base64, error_from_status, parse_json_ptr};
use crate::verification_result::{VerificationResult, VerificationResultPayload};

#[derive(Debug, Clone, PartialEq, Eq)]
/// Wraps `StoreKit.AppTransaction`.
pub struct AppTransaction {
    /// App identifier reported by `StoreKit`.
    pub app_id: Option<u64>,
    /// App transaction identifier reported by `StoreKit`.
    pub app_transaction_id: String,
    /// App version reported by `StoreKit`.
    pub app_version: String,
    /// App version identifier reported by `StoreKit`.
    pub app_version_id: Option<u64>,
    /// Bundle identifier reported by `StoreKit`.
    pub bundle_id: String,
    /// Environment reported by `StoreKit`.
    pub environment: AppStoreEnvironment,
    /// Original app version reported by `StoreKit`.
    pub original_app_version: String,
    /// Original purchase date reported by `StoreKit`.
    pub original_purchase_date: String,
    /// Original platform reported by `StoreKit`.
    pub original_platform: Option<AppStorePlatform>,
    /// Preorder date reported by `StoreKit`.
    pub preorder_date: Option<String>,
    /// Decoded JSON representation returned by `StoreKit`.
    pub json_representation: Vec<u8>,
}

impl AppTransaction {
    /// Fetches `StoreKit.AppTransaction.shared`.
    pub fn shared() -> Result<VerificationResult<Self>, StoreKitError> {
        let mut result_json = ptr::null_mut();
        let mut error_message = ptr::null_mut();
        let status =
            unsafe { ffi::sk_app_transaction_shared(&mut result_json, &mut error_message) };
        if status != ffi::status::OK {
            return Err(unsafe { error_from_status(status, error_message) });
        }
        let payload = unsafe {
            parse_json_ptr::<VerificationResultPayload<AppTransactionPayload>>(
                result_json,
                "app transaction",
            )
        }?;
        payload.into_result(AppTransactionPayload::into_app_transaction)
    }

    /// Fetches `StoreKit.AppTransaction.refresh()`.
    pub fn refresh() -> Result<VerificationResult<Self>, StoreKitError> {
        let mut result_json = ptr::null_mut();
        let mut error_message = ptr::null_mut();
        let status =
            unsafe { ffi::sk_app_transaction_refresh(&mut result_json, &mut error_message) };
        if status != ffi::status::OK {
            return Err(unsafe { error_from_status(status, error_message) });
        }
        let payload = unsafe {
            parse_json_ptr::<VerificationResultPayload<AppTransactionPayload>>(
                result_json,
                "refreshed app transaction",
            )
        }?;
        payload.into_result(AppTransactionPayload::into_app_transaction)
    }
}

#[derive(Debug, Deserialize)]
pub(crate) struct AppTransactionPayload {
    #[serde(rename = "appID")]
    app_id: Option<u64>,
    #[serde(rename = "appTransactionID")]
    app_transaction_id: String,
    #[serde(rename = "appVersion")]
    app_version: String,
    #[serde(rename = "appVersionID")]
    app_version_id: Option<u64>,
    #[serde(rename = "bundleID")]
    bundle_id: String,
    environment: String,
    #[serde(rename = "originalAppVersion")]
    original_app_version: String,
    #[serde(rename = "originalPurchaseDate")]
    original_purchase_date: String,
    #[serde(rename = "originalPlatform")]
    original_platform: Option<String>,
    #[serde(rename = "preorderDate")]
    preorder_date: Option<String>,
    #[serde(rename = "jsonRepresentationBase64")]
    json_representation_base64: String,
}

impl AppTransactionPayload {
    pub(crate) fn into_app_transaction(self) -> Result<AppTransaction, StoreKitError> {
        Ok(AppTransaction {
            app_id: self.app_id,
            app_transaction_id: self.app_transaction_id,
            app_version: self.app_version,
            app_version_id: self.app_version_id,
            bundle_id: self.bundle_id,
            environment: AppStoreEnvironment::from_raw(self.environment),
            original_app_version: self.original_app_version,
            original_purchase_date: self.original_purchase_date,
            original_platform: self.original_platform.map(AppStorePlatform::from_raw),
            preorder_date: self.preorder_date,
            json_representation: decode_base64(
                &self.json_representation_base64,
                "app transaction JSON representation",
            )?,
        })
    }
}
