from __future__ import annotations

import typing
from dataclasses import asdict
from datetime import datetime, timezone

import pandas as pd

from minitrade.broker import Broker, BrokerAccount
from minitrade.utils.mtdb import MTDB

if typing.TYPE_CHECKING:
    from minitrade.trader import RawOrder, TradePlan


class ManualBroker(Broker):
    '''
    ManualBroker is a mock broker that assumes backtest orders will be placed manually.
    '''

    def __init__(self, account: BrokerAccount):
        self.account = account

    def is_ready(self) -> bool:
        return True

    def connect(self):
        pass

    def submit_order(self, plan: TradePlan, order: RawOrder) -> str:
        order_id = MTDB.uniqueid()
        order = asdict(order)
        order['broker_order_id'] = order_id
        order['submit_time'] = datetime.utcnow()
        MTDB.save(order, 'ManualTrade')
        return MTDB.uniqueid()

    def get_account_info(self) -> dict:
        return {'about': 'A mock broker that assumes you will submit orders manually'}

    def get_portfolio(self) -> pd.DataFrame | None:
        trades = MTDB.get_all('ManualTrade', cls=dict)
        df = pd.DataFrame(trades)
        return df.groupby('ticker')['size'].sum() if len(df) > 0 else None

    def download_trades(self) -> pd.DataFrame | None:
        trades = MTDB.get_all('ManualTrade', orderby=('submit_time', False), limit=10, cls=dict)
        return pd.DataFrame(trades) if trades else None

    def download_orders(self) -> pd.DataFrame | None:
        return None

    def disconnect(self) -> None:
        pass

    def resolve_tickers(self, ticker_css: str) -> dict[str, list]:
        return {ticker: [{'id': ticker, 'label': ticker}] for ticker in ticker_css.split(',')}

    def find_trades(self, order: RawOrder) -> dict:
        return MTDB.get_all('ManualTrade', 'id', order.id, cls=dict)

    def find_order(self, order: RawOrder) -> dict:
        return MTDB.get_one('ManualTrade', 'id', order.id, cls=dict)

    def format_trades(self, orders: list[RawOrder]) -> list[dict]:
        trades = []
        for order in orders:
            for status in self.find_trades(order):
                if status['trade_time'] and status['price']:
                    entry_time = status['trade_time'].replace(tzinfo=timezone.utc)
                    trade = {'ticker': order.ticker,
                             'entry_time': entry_time,
                             'size': status['size'],
                             'entry_price': float(status['price']),
                             'commission': float(status['commission'])}
                    trades.append(trade)
        return trades

    def update_trade(self, id, price, commission, trade_time):
        MTDB.update('ManualTrade', 'id', id, values={'price': price,
                    'commission': commission, 'trade_time': trade_time})
