from __future__ import annotations
from os.path import basename
from typing import TYPE_CHECKING, cast

from textual.message import Message
from textual.app import ComposeResult
from textual.screen import Screen
from textual.containers import Vertical
from textual.widgets import (
    Header,
    Footer
)
from textual.binding import Binding

# message box
from analyst_klondike.features.app.open_file_action import open_file
from analyst_klondike.features.code_import.ui_actions.import_task_from_code import (
    import_task_action
)
from analyst_klondike.features.code_import.ui_actions.selectors import select_is_in_teacher_mode
from analyst_klondike.features.data_context.data_state import PythonQuizState
from analyst_klondike.features.events.event_hook import send_event
from analyst_klondike.features.message_box.mb_hook import show_message


# dispatch and state
from analyst_klondike.features.data_context.set_opened_file_action import SetOpenedFileAction
from analyst_klondike.features.update.display_has_later_version import display_message_if_outdated
from analyst_klondike.features.wellcome_message.wellcome_screen import push_wellcome_screen
from analyst_klondike.state.app_dispatch import app_dispatch
from analyst_klondike.state.app_state import (
    AppState,
    get_state,
    select)

# selectors
from analyst_klondike.features.current.selectors import (
    select_current_quiz,
    select_current_task,
    select_has_file_openned
)

# actions
from analyst_klondike.features.app.actions import EditorScreenReadyAction
from analyst_klondike.features.code.macro_commands import run_code_command
from analyst_klondike.features.data_context.save_action import save_to_yaml
from analyst_klondike.features.err_handling import catch_error
from analyst_klondike.features.teacher.clear_solutions import ClearSolutionsAction


# components
from analyst_klondike.ui.editor_screen.components.code_editor import CodeEditor
from analyst_klondike.ui.editor_screen.components.current_task import CurrentTaskInfo
from analyst_klondike.ui.editor_screen.components.explorer import Explorer
from analyst_klondike.ui.editor_screen.components.quiz_description import QuizDescription
from analyst_klondike.ui.editor_screen.components.test_results import TestResults

# screen
from analyst_klondike.ui.file_screen.open_file_screen import OpenFileScreen
import analyst_klondike.features.teacher.create_file as create_file_feature


if TYPE_CHECKING:
    from analyst_klondike.ui.runner_app import RunnerApp


class EditorScreen(Screen[bool]):
    CSS_PATH = "editor.tcss"

    open_file_binding = Binding(id="open_file",
                                key='ctrl+o',
                                key_display='ctrl+o',
                                action='open_quiz_file',
                                description="Открыть",
                                tooltip="Открыть файл с задачами. " +
                                "Вам нужно будет написать код, который пройдет все тесты")

    run_code_binding = Binding(id="run_code",
                               key='f5, ctrl+r',
                               key_display='F5',
                               action='run_btn_click',
                               description="Запустить")

    save_file_binding = Binding(id="save_file",
                                key="ctrl+s",
                                key_display='ctrl+s',
                                action="save_quiz_to_file",
                                description="Сохранить")

    display_help_binding = Binding(id="display_help",
                                   key="f1",
                                   key_display='F1',
                                   action="display_help",
                                   description="Справка",
                                   tooltip="Отобразить справку")

    import_tasks_binding = Binding(id="import_tasks_from_code",
                                   key="f2",
                                   key_display='F2',
                                   action="import_tasks_from_code",
                                   description="Импортировать",
                                   tooltip="Создать новый файл с задачами и \
                                       поместить его в текущий каталог")

    create_file_binding = Binding(id="create_file",
                                  key="ctrl+n",
                                  key_display='Ctrl+N',
                                  action="create_file",
                                  description="Создать файл",
                                  tooltip="Создать тест с задачами")

    clear_solutions_binding = Binding(id="clear_solutions",
                                      key="F11",
                                      key_display='F11',
                                      action="clear_solutions",
                                      description="Удалить решения",
                                      tooltip="Удалить все решения из файла")

    quit_app_binding = Binding(id="quit_app",
                               key="f10",
                               key_display='F10',
                               action="quit_app",
                               description="Выйти",
                               tooltip="Выйти из программы")

    BINDINGS = [
        display_help_binding,
        open_file_binding,
        save_file_binding,
        run_code_binding,
        import_tasks_binding,
        create_file_binding,
        clear_solutions_binding,
        quit_app_binding
    ]

    class UpdateAppTitleMessage(Message):
        def __init__(self, title: str, subtitle: str) -> None:
            super().__init__()
            self.title = title
            self.subtitle = subtitle

    class RequestOpenFileScreen(Message):
        pass

    @property
    def simulator_app(self) -> "RunnerApp":
        sim_app = cast("RunnerApp", self.app)  # type: ignore
        return sim_app

    def _notify_opened(self, fname: str | None) -> None:
        if fname is None:
            return
        self.notify(
            "Загружено",
            title=fname,
            severity="information",
            timeout=1
        )

    async def on_mount(self) -> None:
        app_dispatch(EditorScreenReadyAction())
        push_wellcome_screen()
        open_file(self._notify_opened)
        send_event("app_opened", "/")
        display_message_if_outdated()

    def update_view(self, new_state: AppState):
        if not new_state.is_editor_screen_ready:
            return
        explorer = self.query_one("Explorer", Explorer)
        code_editor = self.query_one("CodeEditor", CodeEditor)
        task_info = self.query_one("CurrentTaskInfo", CurrentTaskInfo)
        test_results = self.query_one("TestResults", TestResults)
        quiz_description = self.query_one("QuizDescription", QuizDescription)

        explorer.state = new_state
        code_editor.state = new_state
        task_info.state = new_state
        test_results.update_view(new_state)
        quiz_description.state = new_state

        # send message to update title and subtitle
        self.post_message(EditorScreen.UpdateAppTitleMessage(
            new_state.current.app_title,
            new_state.current.app_subtitle
        ))
        # update component visibility
        if new_state.current.object_name == "task":
            code_editor.remove_class("component-hidden")
            quiz_description.add_class("component-hidden")
        elif new_state.current.object_name == "quiz":
            code_editor.add_class("component-hidden")
            quiz_description.remove_class("component-hidden")
        elif new_state.current.object_name == "account":
            pass

        # refresh visibility/enable status for footer buttons
        self.refresh_bindings()

    def compose(self) -> ComposeResult:
        yield Header()
        yield Footer(compact=True)
        with Vertical(id="left_panel"):
            yield Explorer()
            yield CurrentTaskInfo()
        with Vertical(id="right_panel"):
            yield CodeEditor()
            yield QuizDescription(classes="component-hidden")
            yield TestResults()

    @catch_error
    def action_run_btn_click(self) -> None:
        run_code_command()

    def action_open_quiz_file(self) -> None:

        @catch_error
        def _on_file_selected(file_path: str | None) -> None:
            if not isinstance(self.simulator_app.screen, EditorScreen):
                return
            if file_path is None or file_path == '':
                return

            app_dispatch(SetOpenedFileAction(
                opened_file_name=basename(file_path),
                opened_file_path=file_path
            ))
            open_file(self._notify_opened)

        self.simulator_app.push_screen(OpenFileScreen(), _on_file_selected)

    @catch_error
    def action_save_quiz_to_file(self) -> None:
        state = get_state()
        save_to_yaml(state)
        self.notify(
            "Сохранено",
            title=state.current.opened_file_name,
            severity="information",
            timeout=1
        )

    @catch_error
    def action_display_help(self) -> None:
        push_wellcome_screen()

    def action_import_tasks_from_code(self) -> None:
        curr_quiz = select(select_current_quiz)

        @catch_error
        def _on_file_selected(file_path: str | None) -> None:
            if file_path is None:
                return
            if curr_quiz is None:
                self.notify(
                    "Необходимо выбрать тест для импортирования",
                    title="Импортирование",
                    severity="error",
                    timeout=1
                )
            assert isinstance(curr_quiz, PythonQuizState)
            import_task_action(curr_quiz.id, file_path)
        if curr_quiz is not None:
            self.simulator_app.push_screen(OpenFileScreen(), _on_file_selected)
        else:
            show_message("Необходимо выбрать тест куда импортировать задачи")

    def action_create_file(self) -> None:
        @catch_error
        def _screen_callback(file_name: str | None) -> None:
            if file_name is not None:
                app_dispatch(
                    create_file_feature.CreateFileAction(file_name)
                )

        create_file_feature.push_screen(_screen_callback)

    @catch_error
    def action_clear_solutions(self) -> None:
        app_dispatch(ClearSolutionsAction())

    def check_action(self, action: str, parameters: tuple[object, ...]):
        if action == EditorScreen.run_code_binding.action:
            curr_task = select(select_current_task)
            if curr_task is not None:
                return True
            return None
        if action == EditorScreen.save_file_binding.action:
            has_file = select(select_has_file_openned)
            if has_file:
                return True
            return None
        if action == EditorScreen.import_tasks_binding.action:
            is_teacher_mode = select(select_is_in_teacher_mode)
            if not is_teacher_mode:
                return False
            curr_quiz = select(select_current_quiz)
            if curr_quiz is not None:
                return True
            return None
        if action in (
                EditorScreen.create_file_binding.action,
                EditorScreen.clear_solutions_binding.action):
            is_teacher_mode = select(select_is_in_teacher_mode)
            return is_teacher_mode
        return True

    async def action_quit_app(self) -> None:
        await self.simulator_app.action_quit()
