import logging
from typing import List, Optional

from textual.containers import Horizontal, Vertical
from textual.css.query import NoMatches
from textual.message import Message
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import (
    Button,
    Footer,
    Header,
    Input,
    RadioButton,
    RadioSet,
    Static,
    TextArea,
)

from models.agent import Agent

logger = logging.getLogger(__name__)


class OTPGenerator(Widget):
    # Reactive properties to track form completion
    requestor_filled = reactive(False)
    reasoning_filled = reactive(False)
    duration_selected = reactive(True)  # Default is selected
    otp_generated = reactive(False)

    class OTPInfo(Message):
        def __init__(
            self,
            devices: Optional[List[Agent]],
            requestor: str,
            reasoning: str,
            duration: int,
        ):
            super().__init__()
            self.devices = devices
            self.requestor = requestor
            self.reasoning = reasoning
            self.duration = duration

    # Duration options in minutes
    DURATION_OPTIONS = [
        (15, "15 minutes"),
        (60, "1 hour"),
        (360, "6 hours"),
        (1440, "1 day"),
        (10080, "7 days"),
    ]

    def __init__(self, devices: List[Agent]):
        """Initialize with a list of Agent objects."""
        super().__init__()
        self.devices = devices

    def watch_requestor_filled(self, old_value: bool, new_value: bool) -> None:
        """Update button state when requestor changes."""
        self._update_button_state()

    def watch_reasoning_filled(self, old_value: bool, new_value: bool) -> None:
        """Update button state when reasoning changes."""
        self._update_button_state()

    def watch_otp_generated(self, old_value: bool, new_value: bool) -> None:
        """Update button state when OTP is generated."""
        self._update_button_state()

    def _update_button_state(self) -> None:
        """Enable/disable the generate button based on form state."""
        try:
            button = self.query_one("#generate_button", Button)
            # Enable only if all fields filled and OTP not yet generated
            button.disabled = not (
                self.requestor_filled
                and self.reasoning_filled
                and not self.otp_generated
            )
        except NoMatches:
            pass

    def compose(self):
        yield Header(show_clock=True, icon="⚙")
        title_text = Static(
            f"🎫 Generate One Time Passes for {len(self.devices)} device(s)",
            id="otpgen_title",
        )
        title_text.styles.margin = (0, 0, 1, 0)
        yield title_text

        with Horizontal() as main_layout:
            main_layout.styles.height = "auto"

            # Left side - Inputs and controls
            with Vertical() as left_side:
                left_side.styles.width = "1fr"
                left_side.styles.height = "auto"

                # Requestor input
                requestor_label = Static("Who is requesting OTP?")
                requestor_label.styles.margin = (0, 0, 0, 0)
                yield requestor_label

                requestor_box = Input(
                    placeholder="Enter requestor name", id="requestor_input"
                )
                requestor_box.styles.margin = (0, 0, 1, 0)
                yield requestor_box

                # Reasoning input
                reasoning_label = Static("What work are they doing?")
                reasoning_label.styles.margin = (0, 0, 0, 0)
                yield reasoning_label

                reasoning_box = Input(
                    placeholder="Enter reason for OTP", id="reasoning_input"
                )
                reasoning_box.styles.margin = (0, 0, 1, 0)
                yield reasoning_box

                # Duration selection
                duration_label = Static("Duration:")
                duration_label.styles.margin = (0, 0, 0, 0)
                yield duration_label

                with RadioSet(id="duration_radio") as radio_set:
                    radio_set.styles.margin = (0, 0, 1, 0)
                    for minutes, label in self.DURATION_OPTIONS:
                        radio = RadioButton(label, id=f"duration_{minutes}")
                        if minutes == 360:  # Default to 6 hours
                            radio.value = True
                        yield radio

                # Buttons in a horizontal layout
                with Horizontal() as button_row:
                    button_row.styles.height = "auto"
                    button_row.styles.margin = (1, 0, 0, 0)

                    back_button = Button("← Back", id="back_button")
                    back_button.styles.width = "1fr"
                    yield back_button

                    generate_button = Button(
                        "Generate OTP", id="generate_button", variant="primary"
                    )
                    generate_button.styles.width = "2fr"
                    yield generate_button

            # Right side - Show device list initially, then output after generation
            with Vertical() as right_side:
                right_side.styles.width = "2fr"
                right_side.styles.height = "100%"

                output_label = Static(
                    f"Selected Devices ({len(self.devices)}):", id="output_label"
                )
                output_label.styles.margin = (0, 0, 0, 0)
                yield output_label

                # Container for either device list or output
                with Vertical(id="output_container") as output_container:
                    output_container.styles.height = "1fr"
                    output_container.styles.margin = (1, 0, 0, 0)
                    output_container.styles.overflow_y = "auto"
                    output_container.styles.border = ("round", "green")

                    # Show device list initially
                    device_list_text = "\n".join(
                        f"• {device.hostname}" for device in self.devices
                    )
                    device_display = Static(device_list_text, id="device_display")
                    yield device_display

                # Copy to clipboard button (hidden initially)
                copy_button = Button("📋 Copy to Clipboard", id="copy_clipboard_button")
                copy_button.styles.margin = (1, 0, 0, 0)
                copy_button.styles.display = "none"
                yield copy_button
        yield Footer()

    def on_mount(self) -> None:
        """Set initial button state."""
        self._update_button_state()

    def on_input_changed(self, event: Input.Changed) -> None:
        """Handle input field changes."""
        input_id = event.input.id

        if input_id == "requestor_input":
            self.requestor_filled = bool(event.value.strip())
        elif input_id == "reasoning_input":
            self.reasoning_filled = bool(event.value.strip())

    def on_button_pressed(self, event: Button.Pressed):
        btn_id = event.button.id

        if btn_id == "back_button":

            while len(self.app.screen_stack) > 2:
                self.app.pop_screen()

            event.stop()

        elif btn_id == "copy_clipboard_button":
            try:
                output_area = self.query_one("#otp_output", TextArea)
                text_to_copy = output_area.text

                import pyperclip

                pyperclip.copy(text_to_copy)
                self.app.notify(
                    "✅ Copied to clipboard!", severity="information", timeout=2
                )
            except ImportError:
                self.app.notify(
                    "⚠️ pyperclip not installed. Run: pip install pyperclip",
                    severity="warning",
                )
            except Exception as e:
                self.app.notify(f"❌ Failed to copy: {str(e)}", severity="error")
            event.stop()

        elif btn_id == "generate_button":
            try:
                requestor = self.query_one("#requestor_input", Input).value.strip()
                reasoning = self.query_one("#reasoning_input", Input).value.strip()

                radio_set = self.query_one("#duration_radio", RadioSet)
                selected_button_id = (
                    radio_set.pressed_button.id if radio_set.pressed_button else None
                )

                if not selected_button_id:
                    self._show_error("Please select a duration")
                    return

                duration = int(selected_button_id.replace("duration_", ""))

                if not requestor or not reasoning:
                    self._show_error("Please fill in all fields")
                    return

                self.otp_generated = True

                # Access API from the app - this is the key change!
                api = self.app.api  # type: ignore

                output_lines = [
                    "Requested OTP Codes:",
                    "=" * 25,
                ]

                otp_dict = {}
                for device in self.devices:
                    try:
                        otp_code = api.otp_generate(device.agentid, duration, reasoning)
                        otp_dict[device.hostname] = otp_code
                        logger.debug(f"Generated OTP for {device.hostname}: {otp_code}")
                    except Exception as e:
                        otp_dict[device.hostname] = f"ERROR: {str(e)}"
                        logger.error(
                            f"Failed to generate OTP for {device.hostname}: {e}"
                        )

                for hostname, otp_code in otp_dict.items():
                    output_lines.append(f"{hostname} | {otp_code}")

                output_lines.append("=" * 25)
                result_text = "\n".join(output_lines)
                self._show_result(result_text)

                # Post message with the OTP info
                self.post_message(
                    self.OTPInfo(self.devices, requestor, reasoning, duration)
                )
                event.stop()

            except NoMatches:
                self._show_error("UI elements not found")
            except Exception as e:
                self._show_error(f"Error: {str(e)}")
                logger.exception("Error generating OTP")

    def _show_error(self, message: str):
        """Display error message in output area."""
        try:
            container = self.query_one("#output_container", Vertical)
            try:
                device_display = self.query_one("#device_display", Static)
                device_display.remove()
            except NoMatches:
                pass

            try:
                output_area = self.query_one("#otp_output", TextArea)
            except NoMatches:
                output_area = TextArea(id="otp_output", read_only=True)
                container.mount(output_area)

            output_area.text = f"❌ ERROR: {message}"
        except Exception as e:
            logger.debug(f"Error showing error message: {e}")

    def _show_result(self, message: str):
        """Display result message in output area."""
        try:
            container = self.query_one("#output_container", Vertical)
            try:
                device_display = self.query_one("#device_display", Static)
                device_display.remove()
            except NoMatches:
                pass

            try:
                output_area = self.query_one("#otp_output", TextArea)
            except NoMatches:
                output_area = TextArea(id="otp_output", read_only=True)
                container.mount(output_area)

            output_area.text = message
            output_label = self.query_one("#output_label", Static)
            output_label.update("Generated OTP Details:")
            copy_button = self.query_one("#copy_clipboard_button", Button)
            copy_button.styles.display = "block"
        except Exception as e:
            logger.debug(f"Error showing result: {e}")

    def display_otp_result(self, result_text: str):
        """Display OTP generation result in the output area."""
        try:
            container = self.query_one("#output_container", Vertical)
            try:
                device_display = self.query_one("#device_display", Static)
                device_display.remove()
            except NoMatches:
                pass

            try:
                output_area = self.query_one("#otp_output", TextArea)
            except NoMatches:
                output_area = TextArea(id="otp_output", read_only=True)
                container.mount(output_area)

            output_area.text = result_text
        except Exception as e:
            logger.debug(f"Error displaying OTP result: {e}")

    def clear_form(self):
        """Clear all input fields and reset state."""
        try:
            self.query_one("#requestor_input", Input).value = ""
            self.query_one("#reasoning_input", Input).value = ""
            self.query_one("#otp_output", TextArea).text = ""
            self.otp_generated = False
            self.requestor_filled = False
            self.reasoning_filled = False
            self._update_button_state()
        except NoMatches:
            pass
