//   Copyright 2023 The Tari Project
//   SPDX-License-Identifier: BSD-3-Clause

use tari_ootle_wallet_sdk::{models::ConfidentialOutputModel, storage::WalletStorageError};
use tari_template_lib_types::{
    EncryptedData,
    crypto::{PedersenCommitmentBytes, RistrettoPublicKeyBytes},
};
use time::PrimitiveDateTime;

use crate::{schema::confidential_outputs, serialization::deserialize_json};

#[derive(Debug, Clone, Identifiable, Queryable)]
#[diesel(table_name = confidential_outputs)]
pub struct ConfidentialOutput {
    pub id: i32,
    pub account_id: i32,
    pub vault_id: i32,
    pub commitment: String,
    // TODO: change to a string to allow arbitrary precision
    pub value: i64,
    pub sender_public_nonce: Option<String>,
    pub view_only_key_id: String,
    pub owner_key_id: Option<String>,
    pub public_asset_tag: Option<String>,
    pub memo_json: Option<String>,
    pub status: String,
    pub locked_at: Option<PrimitiveDateTime>,
    pub locked_by_proof: Option<i32>,
    pub encrypted_data: Vec<u8>,
    pub created_at: PrimitiveDateTime,
    pub updated_at: PrimitiveDateTime,
}

impl ConfidentialOutput {
    pub(crate) fn try_into_output(
        self,
        account_address_str: &str,
        vault_id: &str,
    ) -> Result<ConfidentialOutputModel, WalletStorageError> {
        Ok(ConfidentialOutputModel {
            account_address: account_address_str
                .parse()
                .map_err(|_| WalletStorageError::DecodingError {
                    operation: "try_into_output",
                    item: "output",
                    details: format!("Corrupt db: invalid account address '{}'", account_address_str),
                })?,
            vault_id: vault_id.parse().map_err(|_| WalletStorageError::DecodingError {
                operation: "try_into_output",
                item: "output",
                details: format!("Corrupt db: invalid vault address '{}'", self.vault_id),
            })?,
            commitment: PedersenCommitmentBytes::from_hex(&self.commitment).map_err(|_| {
                WalletStorageError::DecodingError {
                    operation: "outputs_lock_smallest_amount",
                    item: "output commitment",
                    details: "Corrupt db: invalid hex representation".to_string(),
                }
            })?,
            value: (self.value as u64).into(),
            sender_public_nonce: self
                .sender_public_nonce
                .map(|nonce| RistrettoPublicKeyBytes::from_hex(&nonce))
                .transpose()
                .map_err(|_| WalletStorageError::DecodingError {
                    operation: "try_into_output",
                    item: "output",
                    details: "Corrupt db: invalid sender public nonce".to_string(),
                })?,
            view_only_key_id: deserialize_json(&self.view_only_key_id)?,
            owner_key_id: self.owner_key_id.map(|id| deserialize_json(&id)).transpose()?,
            encrypted_data: EncryptedData::try_from(self.encrypted_data).map_err(|len| {
                WalletStorageError::DecodingError {
                    operation: "try_into_output",
                    item: "output",
                    details: format!("Corrupt db: invalid encrypted data length {len}"),
                }
            })?,
            public_asset_tag: self
                .public_asset_tag
                .map(|tag| RistrettoPublicKeyBytes::from_hex(&tag))
                .transpose()
                .map_err(|_| WalletStorageError::DecodingError {
                    operation: "try_into_output",
                    item: "output",
                    details: "Corrupt db: invalid public asset tag".to_string(),
                })?,
            memo: self.memo_json.as_ref().map(deserialize_json).transpose()?,
            status: self.status.parse().map_err(|_| WalletStorageError::DecodingError {
                operation: "try_into_output",
                item: "output",
                details: format!("Corrupt db: invalid output status '{}'", self.status),
            })?,
            lock_id: self.locked_by_proof,
        })
    }
}
