use chik_protocol::Bytes32;
use klvm_traits::{FromKlvm, ToKlvm};
use klvmr::NodePtr;

use crate::Memos;

#[derive(Debug, Clone, PartialEq, Eq, ToKlvm, FromKlvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[klvm(transparent)]
pub struct SettlementPaymentsSolution<T = NodePtr> {
    pub notarized_payments: Vec<NotarizedPayment<T>>,
}

impl SettlementPaymentsSolution {
    pub fn new(notarized_payments: Vec<NotarizedPayment>) -> Self {
        Self { notarized_payments }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, ToKlvm, FromKlvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[klvm(list)]
pub struct NotarizedPayment<T = NodePtr> {
    pub nonce: Bytes32,
    #[klvm(rest)]
    pub payments: Vec<Payment<T>>,
}

impl NotarizedPayment {
    pub fn new(nonce: Bytes32, payments: Vec<Payment>) -> Self {
        Self { nonce, payments }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, ToKlvm, FromKlvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[klvm(list)]
pub struct Payment<T = NodePtr> {
    pub puzzle_hash: Bytes32,
    pub amount: u64,
    #[klvm(rest)]
    pub memos: Memos<T>,
}

impl Payment {
    pub fn new(puzzle_hash: Bytes32, amount: u64, memos: Memos) -> Self {
        Self {
            puzzle_hash,
            amount,
            memos,
        }
    }
}

#[cfg(test)]
mod tests {
    use hex_literal::hex;
    use klvm_utils::tree_hash;
    use klvmr::{serde::node_from_bytes, Allocator};

    use super::*;

    #[test]
    fn test_empty_memos() -> anyhow::Result<()> {
        let mut allocator = Allocator::new();

        /*
        ((0xd951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce1377cad2
            (0x2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b 0x04a817c800 ())
        ))
        */
        let expected_solution = node_from_bytes(
            &mut allocator,
            &hex!(
                "
                ffffa0d951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce13
                77cad2ffffa02a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae972
                67eee629113bff8504a817c800ff80808080
                "
            ),
        )?;

        let nonce = Bytes32::from(hex!(
            "d951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce1377cad2"
        ));
        let puzzle_hash = Bytes32::from(hex!(
            "2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b"
        ));
        let amount = 20_000_000_000;

        let payment = Payment::new(puzzle_hash, amount, Memos::Some(NodePtr::NIL));
        let solution = SettlementPaymentsSolution::new(vec![NotarizedPayment {
            nonce,
            payments: vec![payment],
        }])
        .to_klvm(&mut allocator)?;

        assert_eq!(
            tree_hash(&allocator, solution),
            tree_hash(&allocator, expected_solution)
        );

        Ok(())
    }

    #[test]
    fn test_missing_memos() -> anyhow::Result<()> {
        let mut allocator = Allocator::new();

        /*
        ((0xd951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce1377cad2
            (0x2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b 0x04a817c800)
        ))
        */
        let expected_solution = node_from_bytes(
            &mut allocator,
            &hex!(
                "
                ffffa0d951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce13
                77cad2ffffa02a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae972
                67eee629113bff8504a817c800808080
                "
            ),
        )?;

        let nonce = Bytes32::from(hex!(
            "d951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce1377cad2"
        ));
        let puzzle_hash = Bytes32::from(hex!(
            "2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b"
        ));
        let amount = 20_000_000_000;

        let payment = Payment::new(puzzle_hash, amount, Memos::<NodePtr>::None);
        let solution = SettlementPaymentsSolution::new(vec![NotarizedPayment {
            nonce,
            payments: vec![payment],
        }])
        .to_klvm(&mut allocator)?;

        assert_eq!(
            tree_hash(&allocator, solution),
            tree_hash(&allocator, expected_solution)
        );

        Ok(())
    }
}
