from datetime import datetime
from decimal import Decimal
from operator import attrgetter
import random
import unittest

from monero.address import address
from monero.numbers import PaymentID
from monero.transaction import IncomingPayment, Transaction, Output, _ByHeight
from monero import exceptions


class FiltersTestCase(unittest.TestCase):
    def setUp(self):
        self.tx1 = Transaction(
            timestamp=datetime(2018, 1, 29, 15, 0, 25),
            height=1087606,
            hash="a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14",
            fee=Decimal("0.00352891"),
        )
        self.pm1 = IncomingPayment(
            amount=Decimal("1"),
            local_address=address(
                "Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV"
            ),
            payment_id=PaymentID(
                "0166d8da6c0045c51273dd65d6f63734beb8a84e0545a185b2cfd053fced9f5d"
            ),
            transaction=self.tx1,
        )

        # setup for one-time output tests
        self.json1 = {  # Actual as_json response from TX ee5bcb6430c39757ff27f8d607287572f3956a0ee16bb1d2378891f93746c8f9
            "version": 2,
            "unlock_time": 0,
            "vin": [
                {
                    "key": {
                        "amount": 0,
                        "key_offsets": [
                            25471133,
                            261981,
                            36602,
                            18967,
                            13096,
                            16260,
                            54279,
                            3105,
                            5403,
                            786,
                            555,
                        ],
                        "k_image": "4b48346e954a74be9a334b03cadf8aa020542d201fb6ae7416246d19fd04fdb7",
                    }
                }
            ],
            "vout": [
                {
                    "amount": 0,
                    "target": {
                        "key": "c55e793b4d673dcf73587e5141b777ef24e255d48826c75ce110ffc23ff762b9"
                    },
                },
                {
                    "amount": 0,
                    "target": {
                        "key": "93b263454cd3cc349245ad60c9c248332b885a1f2d7b5792cfc24fd87434d62a"
                    },
                },
            ],
            "extra": [
                1,
                209,
                170,
                43,
                245,
                190,
                68,
                82,
                131,
                116,
                79,
                134,
                175,
                104,
                216,
                127,
                99,
                49,
                127,
                141,
                255,
                65,
                204,
                101,
                81,
                244,
                111,
                253,
                155,
                75,
                111,
                14,
                159,
                2,
                9,
                1,
                24,
                56,
                108,
                94,
                20,
                88,
                150,
                94,
            ],
            "rct_signatures": {
                "type": 5,
                "txnFee": 58560000,
                "ecdhInfo": [
                    {"amount": "6c13cf459cb9ed96"},
                    {"amount": "373bc40c7f600bf4"},
                ],
                "outPk": [
                    "80521a77ebe954a5daa6f14b13cc74337f999bc68177a58e76f768c18f2fa421",
                    "5997e64b90d59f7f810ddbc801f747c4fa43e2de593e4ea48531e16d776c00fd",
                ],
            },
        }
        self.outind1 = [25884175, 25884176]
        self.tx2 = Transaction(json=self.json1, output_indices=self.outind1)
        self.oto1 = Output(index=25973289, amount=Decimal("0.000000000000"))
        self.oto2 = Output(
            pubkey="0faff18f7149a0db5aa0dc3c9116887740ccbb5dc4d1eeff87895288e55e5052"
        )

    def test_hash(self):
        self.assertIn(
            "a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14",
            repr(self.tx1),
        )
        self.assertIn(
            "a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14",
            repr(self.pm1),
        )

    def test_outputs(self):
        out1, out2 = self.tx2.outputs()
        self.assertEqual(out1.transaction, self.tx2)
        self.assertEqual(out2.transaction, self.tx2)
        self.assertIn(self.json1["vout"][0]["target"]["key"], repr(out1))
        self.assertFalse(
            out2 != Output(stealth_address=self.json1["vout"][1]["target"]["key"])
        )
        self.assertIn("(index=25973289,amount=0E-12)", repr(self.oto1))
        self.assertEqual(
            self.oto1, Output(index=25973289, amount=Decimal("0.000000000000"))
        )

        with self.assertRaises(exceptions.TransactionWithoutJSON):
            self.tx1.outputs()

        with self.assertRaises(TypeError):
            self.oto1 == self.oto2


class SortingTestCase(unittest.TestCase):
    def test_sorting(self):
        pmts = [
            IncomingPayment(transaction=Transaction(height=10)),
            IncomingPayment(transaction=Transaction(height=12)),
            IncomingPayment(transaction=Transaction(height=13)),
            IncomingPayment(transaction=Transaction(height=None)),
            IncomingPayment(transaction=Transaction(height=100)),
            IncomingPayment(transaction=Transaction(height=None)),
            IncomingPayment(transaction=Transaction(height=1)),
        ]
        for i in range(1680):  # 1/3 of possible permutations
            sorted_pmts = sorted(pmts, key=_ByHeight)
            self.assertEqual(
                list(
                    map(
                        attrgetter("height"),
                        map(attrgetter("transaction"), sorted_pmts),
                    )
                ),
                [None, None, 100, 13, 12, 10, 1],
            )
            random.shuffle(pmts)
