import sys
import traceback
from typing import Optional

from PyQt6.QtCore import QObject, pyqtSignal

from app.gui.boxes import ErrorBox
from app.gui.windows import (
    AnalysisWindow,
    BrowseFileWindow,
    ConfigurationWindow,
    ResultsWindow,
)
from app.gui.windows.base import DialogWindow


class ExceptionHandler(QObject):
    """
    Class for handling exceptions while running the application.
    """

    errorSignal = pyqtSignal(object, str)

    def __init__(self):
        super().__init__()

    def handler(self, exc_type, exc_value, exc_tb):
        tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
        self.errorSignal.emit(exc_value, tb)
        sys._excepthook(exc_type, exc_value, exc_tb)


class DialogController:
    """
    Class for controlling the flow of the application.
    """

    def __init__(self) -> None:
        self.current_window: Optional[DialogWindow] = None

    def show_browse_file(self) -> None:
        """
        Show the first window of the application - BrowseFileWindow.
        :return:
        """
        self.current_window = self.browse_file = BrowseFileWindow()
        self.browse_file.switch_window.connect(self.show_configuration)
        self.browse_file.show()

    def show_configuration(self) -> None:
        """
        Show the second window of the application - ConfigurationWindow.
        :return:
        """
        self.current_window = self.configuration = ConfigurationWindow(
            file_paths=self.browse_file.file_paths
        )
        self.configuration.switch_window.connect(self.show_analysis)
        self.configuration.show()
        self.browse_file.close()

    def show_analysis(self) -> None:
        """
        Show the third window of the application - AnalysisWindow.
        :return:
        """
        self.current_window = self.analysis = AnalysisWindow(
            file_paths=self.configuration.file_paths,
            configurations=self.configuration.configurations,
        )
        self.analysis.switch_window.connect(self.show_results)
        self.analysis.show()
        self.analysis.start_processing()
        self.configuration.close()

    def show_results(self) -> None:
        """
        Show the fourth window of the application - ResultsWindow.
        :return:
        """
        self.current_window = self.results = ResultsWindow(
            results=self.analysis.results,
            results_path=self.analysis.base_results_folder,
            object_detection_enabled=self.configuration.configurations.object_detection,
        )
        self.results.show()
        self.analysis.close()

    def show_error(self, exc_value: BaseException, tb) -> None:
        """
        Show the error window if an exception occurs.
        :param exc_value: exception object
        :param tb: traceback object
        :return:
        """
        if self.current_window:
            if isinstance(self.current_window, AnalysisWindow):
                self.analysis.processing = False
            self.current_window.close()
        error_box = ErrorBox(exc_value, tb)
        error_box.exec()

    def error_handler(self, exc_type, exc_value, exc_tb) -> None:
        """
        Handle exceptions and show the error window.
        :param exc_type: type of exception object
        :param exc_value: exception object
        :param exc_tb: traceback object
        :return:
        """
        tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
        self.show_error(exc_value, tb)

    def start(self) -> None:
        """
        Entry point of the application.
        :return:
        """
        self.show_browse_file()
