from logging import getLogger, Logger
from os import environ
from typing import Optional

from nextcloud_tasks_api import NextcloudTasksApi, NextcloudTasksApiFactory, ApiError
from textual.app import ComposeResult
from textual.binding import Binding
from textual.reactive import reactive, Reactive
from textual.screen import Screen
from textual.widget import Widget
from textual.widgets import Input, Button, Footer

from .status import StatusBar
from .widget import TaskWidget


class AppError(RuntimeError):
    pass


class MainScreen(Screen[int]):
    BINDINGS = [
        Binding("escape", "quit", "Quit", show=True),
    ]

    def __init__(self, api: NextcloudTasksApi, default_list: Optional[str]) -> None:
        self._api: NextcloudTasksApi = api
        self._status: StatusBar = StatusBar(f"{api.username} @ {api.base_url}")
        self._widget: TaskWidget = TaskWidget(api, self._status, default_list)
        super().__init__()

    def compose(self) -> ComposeResult:
        yield self._status
        yield self._widget
        yield Footer()

    async def on_unmount(self) -> None:
        await self._api.close()

    async def action_quit(self) -> None:
        self.dismiss(0)


class UnlockScreen(Screen[Optional[NextcloudTasksApi]]):
    """Modal startup password prompt, resulting in an instantiated API client."""

    BINDINGS = [
        Binding("escape", "quit", "Quit", show=False),
    ]

    busy: Reactive[bool] = reactive(False)

    class MiddleCenter(Widget):
        """Generic container that centers full viewport."""

        DEFAULT_CSS = """
        MiddleCenter {
            align: center middle;
            height: 1fr;
            width: 1fr;
        }
        MiddleCenter Input {
            width: 100%;
            max-width: 32;
        }
        MiddleCenter Button {
            width: 100%;
            max-width: 30;
            margin: 1 1 0 1;
        }
        """

    def __init__(self, api: NextcloudTasksApiFactory) -> None:
        super().__init__()
        self._logger: Logger = getLogger("Login")
        self._api: NextcloudTasksApiFactory = api
        self._user_input: Input = Input(placeholder="Username", value=environ.get("NEXTCLOUD_USER", ""))
        self._pass_input: Input = Input(placeholder="Password", value=environ.get("NEXTCLOUD_PASS", ""), password=True)
        self._submit: Button = Button(label="Login", variant="primary")
        self._status: StatusBar = StatusBar(self._api.base_url)

    def compose(self) -> ComposeResult:
        yield self._status
        yield UnlockScreen.MiddleCenter(self._user_input, self._pass_input, self._submit)

    def on_mount(self) -> None:
        if self._user_input.value and self._pass_input.value:
            self._do_unlock(self._user_input.value, self._pass_input.value)

    def on_input_submitted(self, evt: Input.Submitted) -> None:
        self._do_unlock(self._user_input.value, self._pass_input.value)

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self._do_unlock(self._user_input.value, self._pass_input.value)

    def on_input_changed(self) -> None:
        self._submit.variant = "primary"

    def _do_unlock(self, username: str, password: str) -> None:
        self.busy = True
        self._submit.variant = "primary"
        self.run_worker(self._try_unlock(username, password))

    def watch_busy(self, busy: bool) -> None:
        self._status.busy = 1 if busy else 0
        self.disabled = busy

    async def _try_unlock(self, username: str, password: str) -> None:
        api: NextcloudTasksApi = await self._api.create(username=username, password=password)

        try:
            await api.open()
            if not [_ async for _ in api.list_user_principal()]:
                raise ApiError("Cannot check for user principal")
        except ApiError as e:
            await api.close()
            self.busy = False
            self._submit.variant = "error"
            self._pass_input.focus()
            self._logger.error(str(e))
            return

        self._submit.variant = "success"  # still disabled
        self.dismiss(api)

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