import logging

from bs4 import Tag
from fastcore.xml import FT
from fasthtml.fastapp import fast_app
from starlette.routing import Mount

from myfasthtml.core.constants import Routes, ROUTE_ROOT
from myfasthtml.test.MyFT import MyFT

utils_app, utils_rt = fast_app()
logger = logging.getLogger("Commands")


def mount_if_not_exists(app, path: str, sub_app):
  """
  Mounts a sub-application only if no Mount object already exists
  at the specified path in the main application's router.
  """
  is_mounted = False
  
  for route in app.router.routes:
    
    if isinstance(route, Mount):
      if route.path == path:
        is_mounted = True
        break
  
  if not is_mounted:
    app.mount(path, app=sub_app)


def merge_classes(*args):
  all_elements = []
  for element in args:
    if element is None or element == '':
      continue
    
    if isinstance(element, (tuple, list, set)):
      all_elements.extend(element)
    
    elif isinstance(element, dict):
      if "cls" in element:
        all_elements.append(element.pop("cls"))
      elif "class" in element:
        all_elements.append(element.pop("class"))
    
    elif isinstance(element, str):
      all_elements.append(element)
    
    else:
      raise ValueError(f"Cannot merge {element} of type {type(element)}")
  
  if all_elements:
    # Remove duplicates while preserving order
    unique_elements = list(dict.fromkeys(all_elements))
    return " ".join(unique_elements)
  else:
    return None


def debug_routes(app):
  def _debug_routes(_app, _route, prefix=""):
    if isinstance(_route, Mount):
      for sub_route in _route.app.router.routes:
        _debug_routes(_app, sub_route, prefix=_route.path)
    else:
      print(f"path={prefix}{_route.path}, methods={_route.methods}, endpoint={_route.endpoint}")
  
  for route in app.router.routes:
    _debug_routes(app, route)


def mount_utils(app):
  """
  Mounts the commands_app to the given application instance if the route does not already exist.

  :param app: The application instance to which the commands_app will be mounted.
  :type app: Any
  :return: Returns the result of the mount operation performed by mount_if_not_exists.
  :rtype: Any
  """
  return mount_if_not_exists(app, ROUTE_ROOT, utils_app)


def get_default_ft_attr(ft):
  """
  for every type of HTML element (ft) gives the default attribute to use for binding
  :param ft:
  :return:
  """
  if ft.tag == "input":
    if ft.attrs.get("type") == "checkbox":
      return "checked"
    elif ft.attrs.get("type") == "radio":
      return "checked"
    elif ft.attrs.get("type") == "file":
      return "files"
    else:
      return "value"
  else:
    return None  # indicate that the content of the FT should be updated


def get_default_attr(data):
  all_attrs = data.__dict__.keys()
  return next(iter(all_attrs))


def is_checkbox(elt):
  if isinstance(elt, (FT, MyFT)):
    return elt.tag == "input" and elt.attrs.get("type", None) == "checkbox"
  elif isinstance(elt, Tag):
    return elt.name == "input" and elt.attrs.get("type", None) == "checkbox"
  else:
    return False


def is_radio(elt):
  if isinstance(elt, (FT, MyFT)):
    return elt.tag == "input" and elt.attrs.get("type", None) == "radio"
  elif isinstance(elt, Tag):
    return elt.name == "input" and elt.attrs.get("type", None) == "radio"
  else:
    return False


def is_select(elt):
  if isinstance(elt, (FT, MyFT)):
    return elt.tag == "select"
  elif isinstance(elt, Tag):
    return elt.name == "select"
  else:
    return False


def is_datalist(elt):
  if isinstance(elt, (FT, MyFT)):
    return elt.tag == "datalist"
  elif isinstance(elt, Tag):
    return elt.name == "datalist"
  else:
    return False


def quoted_str(s):
  if s is None:
    return "None"
  
  if isinstance(s, str):
    if "'" in s and '"' in s:
      return f'"{s.replace('"', '\\"')}"'
    elif '"' in s:
      return f"'{s}'"
    else:
      return f'"{s}"'
  
  return str(s)


@utils_rt(Routes.Commands)
def post(session, c_id: str):
  """
  Default routes for all commands.
  :param session:
  :param c_id:
  :return:
  """
  logger.debug(f"Entering {Routes.Commands} with {session=}, {c_id=}")
  from myfasthtml.core.commands import CommandsManager
  command = CommandsManager.get_command(c_id)
  if command:
    return command.execute()
  
  raise ValueError(f"Command with ID '{c_id}' not found.")


@utils_rt(Routes.Bindings)
def post(session, b_id: str, values: dict):
  """
  Default routes for all bindings.
  :param session:
  :param b_id:
  :param values:
  :return:
  """
  logger.debug(f"Entering {Routes.Bindings} with {session=}, {b_id=}, {values=}")
  from myfasthtml.core.bindings import BindingsManager
  binding = BindingsManager.get_binding(b_id)
  if binding:
    return binding.update(values)
  
  raise ValueError(f"Binding with ID '{b_id}' not found.")
