"""
Layout component for FastHTML applications.

This component provides a responsive layout with fixed header/footer,
optional collapsible left/right drawers, and a scrollable main content area.
"""
import logging
from typing import Literal

from fasthtml.common import *

from myfasthtml.controls.BaseCommands import BaseCommands
from myfasthtml.controls.Boundaries import Boundaries
from myfasthtml.controls.UserProfile import UserProfile
from myfasthtml.controls.helpers import mk
from myfasthtml.core.commands import Command
from myfasthtml.core.dbmanager import DbObject
from myfasthtml.core.instances import SingleInstance
from myfasthtml.core.utils import get_id
from myfasthtml.icons.fluent import panel_left_expand20_regular as left_drawer_icon
from myfasthtml.icons.fluent_p2 import panel_right_expand20_regular as right_drawer_icon

logger = logging.getLogger("LayoutControl")


class LayoutState(DbObject):
  def __init__(self, owner):
    super().__init__(owner)
    with self.initializing():
      self.left_drawer_open: bool = True
      self.right_drawer_open: bool = True
      self.left_drawer_width: int = 250
      self.right_drawer_width: int = 250


class Commands(BaseCommands):
  def toggle_drawer(self, side: Literal["left", "right"]):
    return Command("ToggleDrawer", f"Toggle {side} layout drawer", self._owner.toggle_drawer, side)
  
  def update_drawer_width(self, side: Literal["left", "right"]):
    """
    Create a command to update drawer width.
    
    Args:
        side: Which drawer to update ("left" or "right")
    
    Returns:
        Command: Command object for updating drawer width
    """
    return Command(
      f"UpdateDrawerWidth_{side}",
      f"Update {side} drawer width",
      self._owner.update_drawer_width,
      side
    )


class Layout(SingleInstance):
  """
  A responsive layout component with header, footer, main content area,
  and optional collapsible side drawers.

  Attributes:
      app_name (str): Name of the application
      left_drawer (bool): Whether to include a left drawer
      right_drawer (bool): Whether to include a right drawer
  """
  
  class Content:
    def __init__(self, owner):
      self._owner = owner
      self._content = {}
      self._groups = []
      self._ids = set()
    
    def add_group(self, group, group_ft=None):
      group_ft = group_ft or Div(group, cls="mf-layout-group")
      if not group:
        group_ft = None
      self._groups.append((group, group_ft))
      self._content[group] = []
    
    def add(self, content, group=None):
      content_id = get_id(content)
      if content_id in self._ids:
        return
      
      if group not in self._content:
        self.add_group(group)
        self._content[group] = []
      
      self._content[group].append(content)
      
      if content_id is not None:
        self._ids.add(content_id)
    
    def get_content(self):
      return self._content
    
    def get_groups(self):
      return self._groups
  
  def __init__(self, parent, app_name, _id=None):
    """
    Initialize the Layout component.

    Args:
        app_name (str): Name of the application
        left_drawer (bool): Enable left drawer. Default is True.
        right_drawer (bool): Enable right drawer. Default is True.
    """
    super().__init__(parent, _id=_id)
    self.app_name = app_name
    
    # Content storage
    self._main_content = None
    self._state = LayoutState(self)
    self._boundaries = Boundaries(self)
    self.commands = Commands(self)
    self.left_drawer = self.Content(self)
    self.right_drawer = self.Content(self)
    self.header_left = self.Content(self)
    self.header_right = self.Content(self)
    self.footer_left = self.Content(self)
    self.footer_right = self.Content(self)
  
  def set_footer(self, content):
    """
    Set the footer content.

    Args:
        content: FastHTML component(s) or content for the footer
    """
    self._footer_content = content
  
  def set_main(self, content):
    """
    Set the main content area.

    Args:
        content: FastHTML component(s) or content for the main area
    """
    self._main_content = content
  
  def toggle_drawer(self, side: Literal["left", "right"]):
    logger.debug(f"Toggle drawer: {side=}, {self._state.left_drawer_open=}")
    if side == "left":
      self._state.left_drawer_open = not self._state.left_drawer_open
      return self._mk_left_drawer_icon(), self._mk_left_drawer()
    elif side == "right":
      self._state.right_drawer_open = not self._state.right_drawer_open
      return self._mk_right_drawer_icon(), self._mk_right_drawer()
    else:
      raise ValueError("Invalid drawer side")
  
  def update_drawer_width(self, side: Literal["left", "right"], width: int):
    """
    Update the width of a drawer.
    
    Args:
        side: Which drawer to update ("left" or "right")
        width: New width in pixels
    
    Returns:
        Div: Updated drawer component
    """
    # Constrain width between min and max values
    width = max(150, min(600, width))
    
    logger.debug(f"Update drawer width: {side=}, {width=}")
    
    if side == "left":
      self._state.left_drawer_width = width
      return self._mk_left_drawer()
    elif side == "right":
      self._state.right_drawer_width = width
      return self._mk_right_drawer()
    else:
      raise ValueError("Invalid drawer side")
  
  def _mk_header(self):
    """
    Render the header component.

    Returns:
        Header: FastHTML Header component
    """
    return Header(
      Div(  # left
        self._mk_left_drawer_icon(),
        *self.header_left.get_content(),
        cls="flex gap-1"
      ),
      Div(  # right
        *self.header_right.get_content()[None],
        UserProfile(self),
        cls="flex gap-1"
      ),
      cls="mf-layout-header"
    )
  
  def _mk_footer(self):
    """
    Render the footer component.

    Returns:
        Footer: FastHTML Footer component
    """
    footer_content = self._footer_content if self._footer_content else ""
    return Footer(
      footer_content,
      cls="mf-layout-footer footer sm:footer-horizontal"
    )
  
  def _mk_main(self):
    """
    Render the main content area.

    Returns:
        Main: FastHTML Main component
    """
    main_content = self._main_content if self._main_content else ""
    return Main(
      main_content,
      cls="mf-layout-main"
    )
  
  def _mk_left_drawer(self):
    """
    Render the left drawer if enabled.

    Returns:
        Div: FastHTML Div component for left drawer
    """
    resizer = Div(
      cls="mf-layout-resizer mf-layout-resizer-right",
      data_command_id=self.commands.update_drawer_width("left").id,
      data_side="left"
    )
    
    # Wrap content in scrollable container
    content_wrapper = Div(
      *[
          (
              Div(cls="divider") if index > 0 else None,
              group_ft,
              *[item for item in self.left_drawer.get_content()[group_name]]
          )
          for index, (group_name, group_ft) in enumerate(self.left_drawer.get_groups())
      ],
      cls="mf-layout-drawer-content"
    )
    
    return Div(
      content_wrapper,
      resizer,
      id=f"{self._id}_ld",
      cls=f"mf-layout-drawer mf-layout-left-drawer {'collapsed' if not self._state.left_drawer_open else ''}",
      style=f"width: {self._state.left_drawer_width if self._state.left_drawer_open else 0}px;"
    )
  
  def _mk_right_drawer(self):
    """
    Render the right drawer if enabled.

    Returns:
        Div: FastHTML Div component for right drawer
    """
    resizer = Div(
      cls="mf-layout-resizer mf-layout-resizer-left",
      data_command_id=self.commands.update_drawer_width("right").id,
      data_side="right"
    )
    
    # Wrap content in scrollable container
    content_wrapper = Div(
      *self.right_drawer.get_content(),
      cls="mf-layout-drawer-content"
    )
    
    return Div(
      resizer,
      content_wrapper,
      cls=f"mf-layout-drawer mf-layout-right-drawer {'collapsed' if not self._state.right_drawer_open else ''}",
      id=f"{self._id}_rd",
      style=f"width: {self._state.right_drawer_width if self._state.right_drawer_open else 0}px;"
    )
  
  def _mk_left_drawer_icon(self):
    return mk.icon(right_drawer_icon if self._state.left_drawer_open else left_drawer_icon,
                   id=f"{self._id}_ldi",
                   command=self.commands.toggle_drawer("left"))
  
  def _mk_right_drawer_icon(self):
    return mk.icon(right_drawer_icon if self._state.left_drawer_open else left_drawer_icon,
                   id=f"{self._id}_rdi",
                   command=self.commands.toggle_drawer("right"))
  
  def render(self):
    """
    Render the complete layout.

    Returns:
        Div: Complete layout as FastHTML Div component
    """
    
    # Wrap everything in a container div
    return Div(
      self._mk_header(),
      self._mk_left_drawer(),
      self._mk_main(),
      self._mk_right_drawer(),
      self._mk_footer(),
      Script(f"initLayoutResizer('{self._id}');"),
      id=self._id,
      cls="mf-layout",
    )
  
  def __ft__(self):
    """
    FastHTML magic method for rendering.

    Returns:
        Div: The rendered layout
    """
    return self.render()
