"""Functions for building and modifying the form graph structure."""

from datetime import datetime
from typing import Any

from ..crawling.models import FieldType
from .models import (
    FieldNode,
    FormGraph,
    GraphMetadata,
    PageNode,
    ValueRange,
)


def create_graph(name: str, url: str) -> FormGraph:
    return FormGraph(
        name=name,
        url=url,
        pages=[],
        metadata=GraphMetadata(
            timestamp=datetime.now().isoformat(timespec="seconds"),
            version="1.0.0",
            notes="",
        ),
    )


def add_page(
    graph: FormGraph,
    page_id: str,
    name: str,
    wait_for_selector: str,
) -> PageNode:
    for existing_page in graph.pages:
        if existing_page.page_id == page_id:
            msg = f"Page with id '{page_id}' already exists in graph"
            raise ValueError(msg)

    page = PageNode(
        page_id=page_id,
        name=name,
        wait_for_selector=wait_for_selector,
        fields=[],
        conditionals=[],
    )
    graph.pages.append(page)
    return page


def _find_page(graph: FormGraph, page_id: str) -> PageNode:
    for page in graph.pages:
        if page.page_id == page_id:
            return page
    msg = f"Page '{page_id}' not found in graph"
    raise ValueError(msg)


def set_next_action(graph: FormGraph, page_id: str, selector: str) -> None:
    page = _find_page(graph=graph, page_id=page_id)
    page.next_action_selector = selector


def register_field(
    graph: FormGraph,
    page_id: str,
    field_id: str,
    selector: str,
    field_type: FieldType,
    label: str,
    data_key: str,
    is_required: bool = True,
    iframe_selector: str | None = None,
) -> FieldNode:
    page = _find_page(graph=graph, page_id=page_id)

    for existing_field in page.fields:
        if existing_field.field_id == field_id:
            msg = f"Field with id '{field_id}' already exists on page '{page_id}'"
            raise ValueError(msg)

    field = FieldNode(
        field_id=field_id,
        selector=selector,
        field_type=field_type,
        label=label,
        is_required=is_required,
        data_key=data_key,
        iframe_selector=iframe_selector,
    )
    page.fields.append(field)
    return field


# Maps discovery categories to FieldType
_DISCOVERY_CATEGORY_TO_FIELD_TYPE: dict[str, FieldType] = {
    "text_inputs": FieldType.TEXT,
    "textareas": FieldType.TEXTAREA,
    "selects": FieldType.DROPDOWN,
    "checkboxes": FieldType.CHECKBOX,
    "date_inputs": FieldType.DATE,
    "file_inputs": FieldType.FILE,
    "sliders": FieldType.SLIDER,
}


def import_from_discovery(
    graph: FormGraph,
    page_id: str,
    discovery_report: dict[str, Any],
) -> list[FieldNode]:
    """
    Import fields from a PageDiscoveryReport dict into a graph page.

    Handles flat element lists (text_inputs, textareas, selects, checkboxes,
    date_inputs, file_inputs, sliders), radio_groups, and iframe elements.
    """
    page = _find_page(graph=graph, page_id=page_id)
    imported: list[FieldNode] = []
    field_counter = 0

    for category, field_type in _DISCOVERY_CATEGORY_TO_FIELD_TYPE.items():
        elements = discovery_report.get(category, [])
        for element in elements:
            field_counter += 1
            field_id = _build_field_id(
                element=element,
                page_id=page_id,
                counter=field_counter,
            )
            label = _extract_label(element=element)
            data_key = _build_data_key(element=element, field_id=field_id)

            field = FieldNode(
                field_id=field_id,
                selector=element.get("selector", ""),
                field_type=field_type,
                label=label,
                is_required=True,
                data_key=data_key,
            )
            page.fields.append(field)
            imported.append(field)

    # Radio groups have nested structure
    radio_groups = discovery_report.get("radio_groups", [])
    for group in radio_groups:
        field_counter += 1
        group_name = group.get("group_name", f"radio_group_{field_counter}")
        selector_pattern = group.get("suggested_selector_pattern", f"input[name='{group_name}']")

        field = FieldNode(
            field_id=f"{page_id}_radio_{group_name}",
            selector=selector_pattern,
            field_type=FieldType.RADIO,
            label=group_name,
            is_required=True,
            data_key=group_name,
        )
        page.fields.append(field)
        imported.append(field)

    # Iframe elements
    iframes = discovery_report.get("iframes", [])
    for iframe in iframes:
        iframe_selector = iframe.get("iframe_selector", "")
        iframe_elements = iframe.get("elements", [])
        for element in iframe_elements:
            field_counter += 1
            field_id = _build_field_id(
                element=element,
                page_id=page_id,
                counter=field_counter,
            )
            label = _extract_label(element=element)
            data_key = _build_data_key(element=element, field_id=field_id)

            # Infer field type from the element's suggested_field_type or tag
            suggested = element.get("suggested_field_type")
            field_type_val = FieldType(suggested) if suggested else FieldType.TEXT

            field = FieldNode(
                field_id=field_id,
                selector=element.get("selector", ""),
                field_type=field_type_val,
                label=label,
                is_required=True,
                data_key=data_key,
                iframe_selector=iframe_selector,
            )
            page.fields.append(field)
            imported.append(field)

    return imported


def _build_field_id(element: dict[str, Any], page_id: str, counter: int) -> str:
    element_id = element.get("element_id") or element.get("name")
    if element_id:
        return f"{page_id}_{element_id}"
    return f"{page_id}_field_{counter}"


def _extract_label(element: dict[str, Any]) -> str:
    return (
        element.get("label_text")
        or element.get("aria_label")
        or element.get("placeholder")
        or element.get("name")
        or element.get("element_id")
        or "unknown"
    )


def _build_data_key(element: dict[str, Any], field_id: str) -> str:
    return element.get("name") or element.get("element_id") or field_id


def set_value_range(
    graph: FormGraph,
    page_id: str,
    field_id: str,
    value_range: ValueRange,
) -> None:
    page = _find_page(graph=graph, page_id=page_id)
    for field in page.fields:
        if field.field_id == field_id:
            field.value_range = value_range
            return
    msg = f"Field '{field_id}' not found on page '{page_id}'"
    raise ValueError(msg)
