from abc import abstractmethod
from dataclasses import dataclass
from typing import TypeVar, Generic, Optional, List

from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import Vertical, Horizontal
from textual.reactive import reactive, Reactive
from textual.screen import ModalScreen
from textual.widgets import Button, Label, Checkbox, Input, TextArea

T = TypeVar('T')


class QuestionDialog(Generic[T], ModalScreen[Optional[T]]):
    BINDINGS = [
        Binding("escape", "cancel", "Cancel", show=True),
    ]

    DEFAULT_CSS = """
    QuestionDialog {
        align: center middle;
        height: auto;
        background: $background 1%;
    }

    #dialog {
        width: 75%;
        max-width: 60;
        height: auto;
        padding: 0 1;
        border: thick $accent 50%;
        background: $panel;
    }

    #question {
        width: 100%;
        content-align: center middle;
        margin-bottom: 1;
        text-style: bold;
    }

    CheckBox, ToggleButton {
        background: $panel;
    }

    #buttons {
        align: right middle;
        height: auto;
        margin-top: 1;
    }
    """

    valid: Reactive[bool] = reactive(True)

    def __init__(self, prompt: str, yes: str, no: str) -> None:
        # NB: push_screen must be run from a worker when `wait_for_dismiss` is True
        super().__init__()
        self._prompt: str = prompt
        self._yes: str = yes
        self._no: str = no

    @abstractmethod
    def _compose_dialog(self) -> ComposeResult:
        raise NotImplementedError
        yield

    @abstractmethod
    def _get_result(self) -> T:
        raise NotImplementedError

    def compose(self) -> ComposeResult:
        with Vertical(id="dialog"):
            yield Label(self._prompt, id="question")
            yield from self._compose_dialog()
            yield Horizontal(Button(self._no, variant="error", id="no"),
                             Button(self._yes, variant="primary", id="yes"), id="buttons")

    def watch_valid(self, valid: bool) -> None:
        self.query_one("#yes", Button).disabled = not valid

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "yes":
            self.dismiss(self._get_result())
        else:
            self.dismiss(None)

    def action_cancel(self) -> None:
        self.dismiss(None)


@dataclass(frozen=True)
class CreateAnswer:
    parent: bool
    summary: List[str]


class CreateDialog(QuestionDialog[CreateAnswer]):
    DEFAULT_CSS = """
    TextArea {
        height: auto;
        max-height: 7;
    }
    """

    def __init__(self, parent_id: Optional[str], parent_summary: Optional[str]) -> None:
        super().__init__(prompt="Create task(s)?", yes="Create", no="Cancel")
        self._parent_id: Optional[str] = parent_id
        self._parent_summary: Optional[str] = parent_summary

    def _compose_dialog(self) -> ComposeResult:
        yield TextArea()
        if self._parent_id is not None:
            yield Checkbox(f"Create as subtask of '{self._parent_summary or ''}'")

    def on_mount(self) -> None:
        self.valid = False

    def _parse_text_area(self) -> List[str]:
        return list(filter(None, [_.strip() for _ in self.query_one(TextArea).text.splitlines(keepends=False)]))

    def on_text_area_changed(self, evt: Input.Changed) -> None:
        self.valid = len(self._parse_text_area()) > 0

    def _get_result(self) -> CreateAnswer:
        return CreateAnswer(
            parent=self._parent_id is not None and self.query_one(Checkbox).value,
            summary=self._parse_text_area(),
        )


class CreateListDialog(QuestionDialog[str]):
    def __init__(self) -> None:
        super().__init__(prompt="Create task list?", yes="Create", no="Cancel")
        self._input: Input = Input()

    def _compose_dialog(self) -> ComposeResult:
        yield self._input

    def on_mount(self) -> None:
        self.valid = False

    def _parse_input(self) -> str:
        return self._input.value.strip()

    def on_input_changed(self, evt: Input.Changed) -> None:
        self.valid = len(self._parse_input()) > 0

    def _get_result(self) -> str:
        return self._parse_input()


class DeleteDialog(QuestionDialog[bool]):
    def __init__(self, summary: str, count: int) -> None:
        super().__init__(prompt=f"Delete task '{summary}'?", yes="Delete", no="Cancel")
        self._count: int = count

    def _compose_dialog(self) -> ComposeResult:
        if self._count > 1:
            yield Checkbox(f"Confirm recursive deletion of {self._count} tasks")

    def on_mount(self) -> None:
        self.valid = self._count <= 1

    def on_checkbox_changed(self, evt: Checkbox.Changed) -> None:
        self.valid = evt.value

    def _get_result(self) -> bool:
        return self.valid


class DeleteListDialog(QuestionDialog[bool]):
    def __init__(self, summary: str) -> None:
        super().__init__(prompt=f"Delete task list '{summary}'?", yes="Delete", no="Cancel")

    def _compose_dialog(self) -> ComposeResult:
        yield Checkbox("Confirm recursive deletion of all tasks")

    def on_mount(self) -> None:
        self.valid = False

    def on_checkbox_changed(self, evt: Checkbox.Changed) -> None:
        self.valid = evt.value

    def _get_result(self) -> bool:
        return self.valid