import logging
from unittest.mock import mock_open, patch

import numpy as np
import pytest
from PySide6 import QtWidgets

from sasmodels.sasview_model import load_standard_models

import sas.qtgui.Utilities.GuiUtils as GuiUtils
from sas.qtgui.Plotting.PlotterData import Data1D
from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy

# Local
from sas.qtgui.Utilities.GridPanel import BatchOutputPanel
from sas.sascalc.fit.AbstractFitEngine import FitData1D, FResult

logger = logging.getLogger(__name__)


class BatchOutputPanelTest:
    '''Test the batch output dialog'''
    @pytest.fixture(autouse=True)
    def widget(self, qapp):
        '''Create/Destroy the dialog'''
        # dummy perspective
        class dummy_manager:
            _parent = QtWidgets.QWidget()
            def communicator(self):
                return GuiUtils.communicator
        w = BatchOutputPanel(parent=dummy_manager(), output_data=self.output_for_test())
        test_table = {"p1":[1,2,3],
                      "p2":[4,5,None],
                      "":["a",credits,],
                      "":[]}

        yield w

        '''Destroy the GUI'''
        w.close()

    @staticmethod
    def output_for_test():
        ''' define DATA1D structure for testing'''
        # dummy parameter set
        m = None
        for m in load_standard_models():
            if m.name == "core_shell_ellipsoid":
                model = m()
        assert m is not None
        data = Data1D(x=[1,2], y=[3,4], dx=[0.1, 0.1], dy=[0.,0.])
        fit_data = FitData1D(x=[1,2], y=[3,4], data=data)
        param_list = ['sld_shell', 'sld_solvent']
        output = FResult(model=model, data=fit_data, param_list=param_list)
        output.sas_data = data
        output.theory = np.array([0.1,0.2])
        output.pvec = np.array([0.1, 0.02])
        output.residuals = np.array([0.01, 0.02])
        output.fitness = 9000.0
        output.fitter_id = 200
        output.stderr = [0.001, 0.001]
        output_data = [[output],[output]]
        return output_data

    def testDefaults(self, widget):
        '''Test the GUI in its default state'''
        assert isinstance(widget, QtWidgets.QMainWindow)
        # Default title
        assert widget.windowTitle() == "Batch Fitting Results"

        # non-modal window
        assert not widget.isModal()

    def testActionLoadData(self, widget, mocker):
        '''Test CSV data loader'''
        test_csv = mock_open(read_data = ("""\
File generated by SasView\n
Chi2,Data,scale,background,radius_equat_core,x_core,thick_shell,x_polar_shell,sld_core,sld_shell,sld_solvent,theta,phi,\n
1917.8,cyl_400_40.txt,1,0.001,20,3,30,1,-85.088,-97.636,-92.797,0,0,\n
2.6169,cyl_400_20.txt,1,0.001,20,3,30,1,914.64,906.09,905.67,0,0,\n
"""))
        mocker.patch.object(logger, 'info')
        mocker.patch.object(widget, 'setupTableFromCSV')

        # No filename given
        mocker.patch.object(QtWidgets.QFileDialog, 'getOpenFileName', return_value=[""])
        widget.actionLoadData()
        # Assure parser wasn't called and logging got a message
        logger.info.assert_not_called()

        # Filename given
        mocker.patch.object(QtWidgets.QFileDialog, 'getOpenFileName', return_value="test")
        with patch('builtins.open', test_csv):
            widget.actionLoadData()
            # Assure parser was called
            assert widget.actionLoadData
            widget.setupTableFromCSV.assert_called_once()
            assert "File generated by SasView" in widget.setupTableFromCSV.call_args[0][0][0]

    def notestPlotFits(self, widget):
        '''Test plot generation from selected table rows'''
        # mock tested calls
        #GuiUtils.communicator.plot
        spy_plot_signal = QtSignalSpy(widget.communicator, widget.communicator.plotFromNameSignal)
        # Select row #1
        widget.tblParams.selectRow(0)
        QtWidgets.QApplication.processEvents()

        # See that the signal was emitted
        assert spy_plot_signal.count() == 1
        assert "ddd" in str(spy_plot_signal.called()[0]['args'][0])

    def testDataFromTable(self, widget):
        '''Test dictionary generation from data'''
        params = widget.dataFromTable(widget.tblParams)
        assert len(params) == 13
        assert params['Chi2'][0] == '9000'
        assert params['Data'][1] == 'data'
        assert params['sld_solvent'][1] == '0.02'

    def testActionSendToExcel(self, widget, mocker):
        '''Test Excel bindings'''
        pass

    def testActionSaveFile(self, widget, mocker):
        '''Test file save'''
        mocker.patch.object(widget, 'writeBatchToFile')

        # user cancels dialog
        mocker.patch.object(QtWidgets.QFileDialog, 'getSaveFileName', return_value=("","BOOP"))
        # write not called
        widget.actionSaveFile()
        widget.writeBatchToFile.assert_not_called()

        # user chooses proper name
        mocker.patch.object(QtWidgets.QFileDialog, 'getSaveFileName', return_value=("plop","BOOP"))
        # write called
        widget.actionSaveFile()
        widget.writeBatchToFile.assert_called_once()

    def testSetupTableFromCSV(self, widget):
        '''Test generation of grid table rows from a CSV file'''
        pass
