from dataclasses import dataclass
from typing import Any

import pytest
from fasthtml.components import Input, Label
from fasthtml.fastapp import fast_app

from myfasthtml.controls.helpers import mk
from myfasthtml.core.bindings import Binding
from myfasthtml.core.commands import Command, CommandsManager
from myfasthtml.test.testclient import MyTestClient, TestableElement


def new_value(value):
  return value


@dataclass
class Data:
  value: Any


@pytest.fixture()
def user():
  test_app, rt = fast_app(default_hdrs=False)
  user = MyTestClient(test_app)
  return user


@pytest.fixture()
def rt(user):
  return user.app.route


class TestingCommand:
  def test_i_can_trigger_a_command(self, user):
    command = Command('test', 'TestingCommand', new_value, "this is my new value")
    testable = TestableElement(user, mk.button('button', command))
    testable.click()
    assert user.get_content() == "this is my new value"
  
  def test_error_is_raised_when_command_is_not_found(self, user):
    command = Command('test', 'TestingCommand', new_value, "this is my new value")
    CommandsManager.reset()
    testable = TestableElement(user, mk.button('button', command))
    
    with pytest.raises(ValueError) as exc_info:
      testable.click()
    
    assert "not found." in str(exc_info.value)
  
  def test_i_can_play_a_complex_scenario(self, user, rt):
    command = Command('test', 'TestingCommand', new_value, "this is my new value")
    
    @rt('/')
    def get(): return mk.button('button', command)
    
    user.open("/")
    user.should_see("button")
    
    user.find_element("button").click()
    user.should_see("this is my new value")


class TestingBindings:
  def test_i_can_bind_input(self, user, rt):
    @rt("/")
    def index():
      data = Data("hello world")
      input_elt = Input(name="input_name")
      label_elt = Label()
      mk.manage_binding(input_elt, Binding(data))
      mk.manage_binding(label_elt, Binding(data))
      return input_elt, label_elt
    
    user.open("/")
    user.should_see("")
    testable_input = user.find_element("input")
    testable_input.send("new value")
    user.should_see("new value")  # the one from the label
