import datetime
from decimal import Decimal
from pathlib import Path

import fin_defs
import fin_depo

import crypto_seller
import crypto_seller.order_csv


class SellerMock(fin_depo.data.DepoFetcher):
    def __init__(self, asset: fin_defs.Asset, initial_amount: Decimal):
        self.asset = asset
        self.amount_left = initial_amount

    def get_depo(self) -> fin_depo.data.Depo:
        return fin_depo.data.DepoSingle(
            'TestDepo',
            datetime.datetime.now(tz=datetime.UTC),
            {self.asset: self.amount_left},
        )

    def place_market_order(
        self,
        input_asset: fin_defs.Asset,
        input_amount: Decimal,
        output_asset: fin_defs.Asset,
    ) -> fin_depo.data.TradeOrderDetails:
        assert input_amount <= self.amount_left, 'Attempt to sell too much'
        self.amount_left -= input_amount

        executed_time = datetime.datetime.now(tz=datetime.UTC)

        return fin_depo.data.TradeOrderDetails(
            executed_time=executed_time,
            input_asset=input_asset,
            input_amount=input_amount,
            output_asset=output_asset,
            output_amount=input_amount,
            fee_asset=input_asset,
            fee_amount=Decimal(0),
            order_id=Decimal(10000000 - self.amount_left),
            raw_order_details={'TEST': 1, 'DATA': 2},
        )


class SleepMock:
    def __init__(self):
        self.time_slept = 0.0

    def __call__(self, amount: float):
        self.time_slept = self.time_slept + amount


def test_auto_run():
    sleep_mock = SleepMock()
    seller_mock = SellerMock(fin_defs.USDT, Decimal('1000'))

    config = crypto_seller.AutoSellConfig(
        input_amount_range=(Decimal('1'), Decimal('1')),
        interval_range=(datetime.timedelta(seconds=1), datetime.timedelta(seconds=1)),
        input_asset=fin_defs.USDT,
        output_asset=fin_defs.USD,
        exit_when_empty=True,
        seller=seller_mock,
        log_order_to_csv=crypto_seller.order_csv.CsvFileLogger(
            Path('test/output/test-trades.csv'),
        ),
        sleep=sleep_mock,
    )

    crypto_seller.log_estimates(config)

    results = crypto_seller.run_auto_sell(config)

    # Check results
    assert len(results.order_details) == 1000
    assert results.total_sleep_duration == datetime.timedelta(seconds=1000)
    assert results.total_input_amount() == 1000
    assert results.total_output_amount() == 1000

    # Check mocks agree
    assert seller_mock.amount_left == 0
    assert sleep_mock.time_slept == 1000 + 17.2