"""
Author: Espoir Loém

This module provides hooks and utilities for rendering views and components in the Nexy application.
"""

from pathlib import Path
import re
from fastapi.responses import HTMLResponse
from jinja2 import Environment, FileSystemLoader, TemplateNotFound
from markupsafe import Markup
from jinja2.ext import Extension

def find_layouts(path):
    """
    Finds layout.html files by traversing up from the specified path to 'app'.
    Returns layouts in nesting order (app -> deeper).
    """
    layouts = []
    path_obj = Path(path)

    while path_obj.parts:
        current_path = Path(*path_obj.parts)
        layout_file = current_path / "layout.html"

        if layout_file.exists():
            layouts.append(str(layout_file).replace("\\", "/"))

        if path_obj.parts[-1] == "app":
            break

        path_obj = path_obj.parent

    return layouts[::-1]  # Reverse layouts to apply from root to leaf


def replace_block_component(match):
    """
    Replaces block components in a template with a specific format.
    Handles attributes and nested components.
    """
    component_name = match.group(1)
    children = match.group(3) or ""
    attrs_str = match.group(2) or ""
    attrs = {
        attr.group(1): attr.group(2)[2:-2].strip() if attr.group(2).startswith("{{") and attr.group(2).endswith("}}") else f'"{attr.group(2)}"'
        for attr in re.finditer(r'(\w+)=["\']?([^"\'>]+)["\']?', attrs_str)
    }

    children = re.sub(r'<([A-Za-z]+)( [^>]*)?>(.*?)</\1>', replace_block_component, children, flags=re.DOTALL)

    if component_name[0].isupper():
        attrs_str = ", ".join(f"{name}={value}" for name, value in attrs.items())
        return f"@call {component_name}({attrs_str})!\n{children}\n@endcall!" if attrs_str else f"@call {component_name}!\n{children}\n@endcall!"

    return match.group(0)


def replace_self_closing(match):
    """
    Replaces self-closing components in a template with a specific format.
    Handles attributes.
    """
    component_name = match.group(1)
    attrs_str = match.group(2) or ""
    attrs = {
        attr.group(1): attr.group(2)[2:-2].strip() if attr.group(2).startswith("{{") and attr.group(2).endswith("}}") else f'"{attr.group(2)}"'
        for attr in re.finditer(r'(\w+)=["\']?([^"\'>]+)["\']?', attrs_str)
    }

    if component_name[0].isupper():
        attrs_str = ", ".join(f"{name}={value}" for name, value in attrs.items())
        return f"{{{{ {component_name}({attrs_str}) }}}}"

    return match.group(0)


def componentsParser(template):
    """
    Parses a template to replace custom components with a specific format.
    Handles both block and self-closing components.
    """
    if re.search(r'<[A-Z][a-zA-Z]*', template):
        template = re.sub(r'<([A-Za-z]+)( [^>]*)?>(.*?)</\1>', replace_block_component, template, flags=re.DOTALL)
        template = re.sub(r'<([A-Za-z]+)( [^>]*)?/>', replace_self_closing, template)
    return Markup(template)


class ComponentExtension(Extension):
    def preprocess(self, source, name, filename=None):
        """
        Preprocesses the source to transform custom components.
        """
        return re.sub(r'<([A-Za-z]+)( [^>]*)?>(.*?)</\1>', replace_block_component, source, flags=re.DOTALL)


env = Environment(
    loader=FileSystemLoader("."),
    auto_reload=True,
    block_start_string="{!",
    block_end_string="}",
    trim_blocks=True,
    lstrip_blocks=True,
    comment_start_string="<!--",
    comment_end_string="-->",
    extensions=[ComponentExtension]
)

def useView(data, path):
    """
    Renders a view with its hierarchically nested layouts.
    :param data: Data to pass to templates
    :param path: View path (relative to app folder)
    """
    try:
        layouts = find_layouts(f"{path}/")
        view = f"{path}/view.html"
        
        view_template = env.get_template(view) 
        
        content = view_template.render(**data)
        
        for layout_path in layouts:
            layout_template = env.get_template(layout_path)
            content = layout_template.render(children=content, **data)
        
        return HTMLResponse(content=content)
    
    except TemplateNotFound as e:
        try:
            error_template = env.get_template(f"{path}/404.html")
            return HTMLResponse(content=error_template.render(error=str(e)), status_code=404)
        except TemplateNotFound:
            return HTMLResponse(content=f"Template not found: {str(e)}", status_code=404)

    except Exception as e:
        try:
            error_template = env.get_template("errors/500.html")
            return HTMLResponse(
                content=error_template.render(error=str(e)), 
                status_code=500
            )
        except TemplateNotFound:
            return HTMLResponse(content=f"Error: {str(e)}", status_code=500)

def useActionView(data, path):
    """
    Renders view for action responses.
    """
    try:
        view_template = env.get_template(f"{path}/view.html")
        preprocessed_view_content = componentsParser(view_template.render())
        content = preprocessed_view_content.render(**data)
        return HTMLResponse(content=content)
    except TemplateNotFound:
        return data

class State:
    """
    A simple state management class.
    """
    def __init__(self, initial_value):
        self.value = initial_value
        
    def get(self):
        return self.value
    
    def set(self, new_value):
        self.value = new_value
        return self.value

class Fetch:
    """
    A placeholder class for future fetch-related functionality.
    """
    def __init__(self):
        pass