"""Ui."""

import logging
import logging.config
import os.path
from datetime import datetime

from PyQt5 import QtCore, QtGui

from .lib import (
    EquityChart,
    OptimizatimizedResultsTable,
    OptimizationTable,
    Portfolio,
    QuotesChart,
    ResultsTable,
    Settings,
    Symbol,
    TradesTable,
    get_quotes,
    get_symbols,
    strategies_from_file,
)

__all__ = ('MainWidget',)


logger = logging.getLogger(__name__)

DEFAULT_TICKER = 'AAPL'
SYMBOL_COLUMNS = ['Symbol', 'Security Name']


class SymbolsLoaderThread(QtCore.QThread):

    symbols_loaded = QtCore.pyqtSignal(object)

    def run(self):
        symbols = get_symbols()
        self.symbols_loaded.emit(symbols[SYMBOL_COLUMNS].values)


class DataTabWidget(QtGui.QWidget):

    data_updated = QtCore.pyqtSignal(object)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.select_source = QtGui.QTabWidget(self)
        self.select_source.setGeometry(210, 50, 340, 200)

        self.init_shares_tab_ui()
        self.init_external_tab_ui()

        self.symbols_loader = SymbolsLoaderThread()
        self.symbols_loader.started.connect(self.on_symbols_loading)
        self.symbols_loader.symbols_loaded.connect(
            self.on_symbols_loaded, QtCore.Qt.QueuedConnection
        )
        self.symbols_loader.start()

        self.date_from = self.shares_date_from.date().toPyDate()
        self.date_to = self.shares_date_to.date().toPyDate()

    def init_external_tab_ui(self):
        """External data."""
        self.external_tab = QtGui.QWidget()
        self.external_tab.setEnabled(False)
        self.external_layout = QtGui.QVBoxLayout(self.external_tab)

        self.import_data_name = QtGui.QLabel('Import External Data')
        self.import_data_label = QtGui.QLabel('...')
        self.import_data_btn = QtGui.QPushButton('Import')
        self.import_data_btn.clicked.connect(self.open_file)

        self.external_layout.addWidget(
            self.import_data_name, 0, QtCore.Qt.AlignCenter
        )
        self.external_layout.addWidget(
            self.import_data_label, 0, QtCore.Qt.AlignCenter
        )
        self.external_layout.addWidget(
            self.import_data_btn, 0, QtCore.Qt.AlignCenter
        )

        self.select_source.addTab(self.external_tab, 'Custom data')

    def init_shares_tab_ui(self):
        """Shares."""
        self.shares_tab = QtGui.QWidget()
        self.shares_layout = QtGui.QFormLayout(self.shares_tab)
        today = datetime.today()

        self.shares_date_from = QtGui.QDateEdit()
        self.shares_date_from.setMinimumDate(QtCore.QDate(1900, 1, 1))
        self.shares_date_from.setMaximumDate(QtCore.QDate(2030, 12, 31))
        self.shares_date_from.setDate(QtCore.QDate(today.year, 1, 1))
        self.shares_date_from.setDisplayFormat('dd.MM.yyyy')

        self.shares_date_to = QtGui.QDateEdit()
        self.shares_date_to.setMinimumDate(QtCore.QDate(1900, 1, 1))
        self.shares_date_to.setMaximumDate(QtCore.QDate(2030, 12, 31))
        self.shares_date_to.setDate(
            QtCore.QDate(today.year, today.month, today.day)
        )
        self.shares_date_to.setDisplayFormat('dd.MM.yyyy')

        self.shares_symbol_list = QtGui.QComboBox()
        self.shares_symbol_list.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.shares_symbol_list.setMaxVisibleItems(20)
        self.shares_symbol_list.setEditable(True)

        self.shares_show_btn = QtGui.QPushButton('Load')
        self.shares_show_btn.clicked.connect(self.update_data)

        self.shares_layout.addRow('From', self.shares_date_from)
        self.shares_layout.addRow('To', self.shares_date_to)
        self.shares_layout.addRow('Symbol', self.shares_symbol_list)
        self.shares_layout.addRow(None, self.shares_show_btn)

        self.select_source.addTab(self.shares_tab, 'Shares/Futures/ETFs')

    def on_symbols_loading(self):
        self.shares_symbol_list.addItem('Loading...')
        self.shares_symbol_list.setEnabled(False)

    def on_symbols_loaded(self, symbols):
        self.shares_symbol_list.clear()
        self.shares_symbol_list.setEnabled(True)
        # self.symbols = ['%s/%s' % (ticker, name) for ticker, name in symbols]
        # self.shares_symbol_list.addItems(self.symbols)
        model = QtGui.QStandardItemModel()
        model.setHorizontalHeaderLabels(SYMBOL_COLUMNS)
        for irow, (ticker, name) in enumerate(symbols):
            model.setItem(irow, 0, QtGui.QStandardItem(ticker))
            model.setItem(irow, 1, QtGui.QStandardItem(name))

        table_view = QtGui.QTableView()
        table_view.setModel(model)
        table_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        table_view.verticalHeader().setVisible(False)
        table_view.setAutoScroll(False)
        table_view.setShowGrid(False)
        table_view.resizeRowsToContents()
        table_view.setColumnWidth(0, 60)
        table_view.setColumnWidth(1, 240)
        table_view.setMinimumWidth(300)

        completer = QtGui.QCompleter(model)
        completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        completer.setModel(model)

        self.symbols = symbols
        self.shares_symbol_list.setModel(model)
        self.shares_symbol_list.setView(table_view)
        self.shares_symbol_list.setCompleter(completer)

        # set default symbol
        self.shares_symbol_list.setCurrentIndex(
            self.shares_symbol_list.findText(DEFAULT_TICKER)
        )

    def open_file(self):
        filename = QtGui.QFileDialog.getOpenFileName(
            parent=None,
            caption='Open a source of data',
            directory=QtCore.QDir.currentPath(),
            filter='All (*);;Text (*.txt)',
        )

        self.import_data_label.setText('Loading %s' % filename)

        with open(filename, 'r', encoding='utf-8') as f:
            self.data = f.readlines()

    def update_data(self, ticker=None):
        ticker = ticker or self.shares_symbol_list.currentText()
        self.symbol = Symbol(ticker=ticker, mode=Symbol.SHARES)
        self.date_from = self.shares_date_from.date().toPyDate()
        self.date_to = self.shares_date_to.date().toPyDate()

        get_quotes(
            symbol=self.symbol.ticker,
            date_from=self.date_from,
            date_to=self.date_to,
        )

        self.data_updated.emit(self.symbol)


class StrategyBoxWidget(QtGui.QGroupBox):

    run_backtest = QtCore.pyqtSignal(object)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setTitle('Strategy')
        self.setAlignment(QtCore.Qt.AlignCenter)
        self.layout = QtGui.QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

        self.list = QtGui.QComboBox()

        self.add_btn = QtGui.QPushButton('+')
        self.add_btn.clicked.connect(self.add_strategies)

        self.start_btn = QtGui.QPushButton('Start Backtest')
        self.start_btn.clicked.connect(self.load_strategy)

        self.layout.addWidget(self.list, stretch=2)
        self.layout.addWidget(self.add_btn, stretch=0)
        self.layout.addWidget(self.start_btn, stretch=0)

        self.load_strategies_from_settings()

    def reload_strategies(self):
        """Reload user's file to get actual version of the strategies."""
        self.strategies = strategies_from_file(self.strategies_path)

    def reload_list(self):
        self.list.clear()
        self.list.addItems([s.get_name() for s in self.strategies])

    def load_strategies_from_settings(self):
        filename = Settings.value('strategies/path', None)
        if not filename or not os.path.exists(filename):
            return
        self.strategies_path = filename
        self.reload_strategies()
        self.reload_list()

    def save_strategies_to_settings(self):
        Settings.setValue('strategies/path', self.strategies_path)

    def add_strategies(self):
        filename, _filter = QtGui.QFileDialog.getOpenFileName(
            self,
            caption='Open Strategy.',
            directory=QtCore.QDir.currentPath(),
            filter='Python modules (*.py)',
        )
        if not filename:
            return
        self.strategies_path = filename
        self.save_strategies_to_settings()
        self.reload_strategies()
        self.reload_list()

    def load_strategy(self):
        self.reload_strategies()
        self.run_backtest.emit(self.strategies[self.list.currentIndex()])


class QuotesTabWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.layout = QtGui.QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.toolbar_layout = QtGui.QHBoxLayout()
        self.toolbar_layout.setContentsMargins(10, 10, 15, 0)
        self.chart_layout = QtGui.QHBoxLayout()

        self.init_timeframes_ui()
        self.init_strategy_ui()

        self.layout.addLayout(self.toolbar_layout)
        self.layout.addLayout(self.chart_layout)

    def init_timeframes_ui(self):
        self.tf_layout = QtGui.QHBoxLayout()
        self.tf_layout.setSpacing(0)
        self.tf_layout.setContentsMargins(0, 12, 0, 0)
        time_frames = ('1M', '5M', '15M', '30M', '1H', '1D', '1W', 'MN')
        btn_prefix = 'TF'
        for tf in time_frames:
            btn_name = ''.join([btn_prefix, tf])
            btn = QtGui.QPushButton(tf)
            # TODO:
            btn.setEnabled(False)
            setattr(self, btn_name, btn)
            self.tf_layout.addWidget(btn)
        self.toolbar_layout.addLayout(self.tf_layout)

    def init_strategy_ui(self):
        self.strategy_box = StrategyBoxWidget(self)
        self.toolbar_layout.addWidget(self.strategy_box)

    def update_chart(self, symbol):
        if not self.chart_layout.isEmpty():
            self.chart_layout.removeWidget(self.chart)
        self.chart = QuotesChart()
        self.chart.plot(symbol)
        self.chart_layout.addWidget(self.chart)

    def add_signals(self):
        self.chart.add_signals()


class EquityTabWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.layout = QtGui.QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

    def update_chart(self):
        if not self.layout.isEmpty():
            self.layout.removeWidget(self.chart)
        self.chart = EquityChart()
        self.chart.plot()
        self.layout.addWidget(self.chart)


class ResultsTabWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.layout = QtGui.QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

    def update_table(self):
        if not self.layout.isEmpty():
            self.layout.removeWidget(self.table)
        self.table = ResultsTable()
        self.table.plot()
        self.layout.addWidget(self.table)


class TradesTabWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.layout = QtGui.QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

    def update_table(self):
        if not self.layout.isEmpty():
            self.layout.removeWidget(self.table)
        self.table = TradesTable()
        self.table.plot()
        self.layout.addWidget(self.table)


class OptimizationTabWidget(QtGui.QWidget):

    optimization_done = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.layout = QtGui.QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.table_layout = QtGui.QHBoxLayout()
        self.top_layout = QtGui.QHBoxLayout()
        self.top_layout.setContentsMargins(0, 10, 0, 0)

        self.start_optimization_btn = QtGui.QPushButton('Start')
        self.start_optimization_btn.clicked.connect(self.start_optimization)
        self.top_layout.addWidget(
            self.start_optimization_btn, alignment=QtCore.Qt.AlignRight
        )

        self.layout.addLayout(self.top_layout)
        self.layout.addLayout(self.table_layout)

    def update_table(self, strategy):
        if not self.table_layout.isEmpty():
            # close() to avoid an UI issue with duplication of the table
            self.table.close()
            self.table_layout.removeWidget(self.table)
        self.table = OptimizationTable()
        self.table.plot(strategy)
        self.table_layout.addWidget(self.table)

    def start_optimization(self, *args, **kwargs):
        logger.debug('Start optimization')
        self.table.optimize()
        self.optimization_done.emit()
        logger.debug('Optimization is done')


class OptimizatimizedResultsTabWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.layout = QtGui.QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

        self.table = OptimizatimizedResultsTable()
        self.table.plot()

        self.layout.addWidget(self.table)


class MainWidget(QtGui.QTabWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setDocumentMode(True)

        self.data_tab = DataTabWidget(self)
        self.data_tab.data_updated.connect(self._update_quotes_chart)
        self.addTab(self.data_tab, 'Data')

    def _add_quotes_tab(self):
        if self.count() >= 2:  # quotes tab is already exists
            return
        self.quotes_tab = QuotesTabWidget(self)
        self.quotes_tab.strategy_box.run_backtest.connect(self._run_backtest)
        self.addTab(self.quotes_tab, 'Quotes')

    def _add_result_tabs(self):
        if self.count() >= 3:  # tabs are already exist
            return
        self.equity_tab = EquityTabWidget(self)
        self.results_tab = ResultsTabWidget(self)
        self.trades_tab = TradesTabWidget(self)
        self.optimization_tab = OptimizationTabWidget(self)
        self.optimization_tab.optimization_done.connect(
            self._add_optimized_results
        )  # noqa
        self.addTab(self.equity_tab, 'Equity')
        self.addTab(self.results_tab, 'Results')
        self.addTab(self.trades_tab, 'Trades')
        self.addTab(self.optimization_tab, 'Optimization')

    def _update_quotes_chart(self, symbol):
        self._add_quotes_tab()
        self.symbol = symbol
        self.quotes_tab.update_chart(self.symbol)
        self.setCurrentIndex(1)

    def _run_backtest(self, strategy):
        logger.debug('Run backtest')
        Portfolio.clear()

        stg = strategy(symbols=[self.symbol])
        stg.run()

        Portfolio.summarize()
        self.quotes_tab.add_signals()
        self._add_result_tabs()
        self.equity_tab.update_chart()
        self.results_tab.update_table()
        self.trades_tab.update_table()
        self.optimization_tab.update_table(strategy=stg)
        logger.debug(
            'Count positions in the portfolio: %d', Portfolio.position_count()
        )

    def _add_optimized_results(self):
        self.addTab(OptimizatimizedResultsTabWidget(self), 'Optimized Results')
        self.setCurrentIndex(self.count() - 1)

    def plot_test_data(self):
        logger.debug('Plot test data')
        self.data_tab.update_data(ticker=DEFAULT_TICKER)
        self.quotes_tab.strategy_box.load_strategy()