import logging
from typing import Any, Dict, List, Optional

from .models import (
    RiffAppPlan,
    RiffComponent,
    RiffComponentProps,
    RiffDataSource,
    RiffLayout,
    RiffPage,
    RiffStyle,
)

logger = logging.getLogger(__name__)


VISUAL_TYPE_MAPPING = {
    "clusteredColumnChart": "BarChart",
    "stackedColumnChart": "StackedBarChart",
    "clusteredBarChart": "BarChart",
    "stackedBarChart": "StackedBarChart",
    "lineChart": "LineChart",
    "areaChart": "AreaChart",
    "stackedAreaChart": "StackedAreaChart",
    "pieChart": "PieChart",
    "donutChart": "DonutChart",
    "card": "StatCard",
    "multiRowCard": "StatGrid",
    "slicer": "FilterSelect",
    "tableEx": "DataTable",
    "pivotTable": "DataTable",
}


def sanitize_name(name: str) -> str:
    """Sanitizes strings for use in code identifiers."""
    return "".join(c if c.isalnum() else "_" for c in name).lower()


def infer_component_props(visual: Dict[str, Any], riff_type: str) -> RiffComponentProps:
    """
    Infers Riff component props based on visual metadata.
    Uses type-based inference: String/Date -> Axis/Category, Numeric -> Value/Series.
    """
    title = visual.get("title", "Untitled")
    query_meta = visual.get("query_metadata", {})
    selects = query_meta.get("Select", [])

    props = RiffComponentProps(title=title, type=riff_type)

    dimensions = []
    measures = []

    for col in selects:
        col_name = col.get("name", "Unknown")
        col_type = col.get("type", "unknown")

        if col_type in ["measure", "column_agg"]:
            measures.append(col_name)
        elif col_type in ["column_hierarchy"]:
            dimensions.append(col_name)
        else:
            if col.get("func") is not None:
                measures.append(col_name)
            else:
                dimensions.append(col_name)

    if riff_type in ["BarChart", "StackedBarChart", "LineChart", "AreaChart", "StackedAreaChart"]:
        if dimensions:
            props.xAxis = dimensions[0]
        if measures:
            props.yAxis = measures
    elif riff_type in ["PieChart", "DonutChart"]:
        if dimensions:
            props.dataKey = dimensions[0]
        if measures:
            props.valueKey = measures[0]
    elif riff_type in ["StatCard", "StatGrid"]:
        if measures:
            props.valueKey = measures[0]
    elif riff_type in ["DataTable"]:
        props.yAxis = [col.get("name") for col in selects]
    elif riff_type in ["FilterSelect"]:
        if dimensions:
            props.xAxis = dimensions[0]

    return props


def extract_style(style_data: Dict[str, Any]) -> RiffStyle:
    """Extracts standardized style properties."""
    style = RiffStyle()

    bg = style_data.get("background", {})
    if isinstance(bg, dict):
        if "color" in bg:
            style.background_color = bg["color"].get("hex")
        style.transparency = bg.get("transparency")

    title = style_data.get("title", {})
    if isinstance(title, dict) and "color" in title:
        style.title_color = title["color"].get("hex")

    value = style_data.get("value", {})
    if isinstance(value, dict) and "color" in value:
        style.value_color = value["color"].get("hex")

    return style


def extract_layout(layout_data: Dict[str, Any]) -> Optional[RiffLayout]:
    """Extracts standardized layout properties."""
    if not layout_data:
        return None

    return RiffLayout(
        x=layout_data.get("x", 0),
        y=layout_data.get("y", 0),
        width=layout_data.get("width", 0),
        height=layout_data.get("height", 0),
        z=layout_data.get("z", 0),
        tabOrder=layout_data.get("tabOrder"),
    )


def generate_endpoint_name(page_name: str, visual_title: str) -> str:
    """Generates a suggested API endpoint name."""
    p_name = sanitize_name(page_name)
    v_name = sanitize_name(visual_title)
    return f"get_{p_name}_{v_name}"[:50]


def generate_plan(analysis_result: Dict[str, Any]) -> RiffAppPlan:
    """
    Main function to convert analysis result into a Riff App Plan.
    """
    parsed_layout = analysis_result.get("parsed_layout", {})
    pages_data = parsed_layout.get("pages", [])

    riff_pages = []

    for page in pages_data:
        page_name = page.get("name", "Untitled Page")
        page_id = page.get("id", "unknown_page")

        page_style = extract_style(page.get("style", {}))

        riff_components = []

        for visual in page.get("visuals", []):
            v_type = visual.get("type")
            v_title = visual.get("title", "Untitled")

            riff_type = VISUAL_TYPE_MAPPING.get(v_type)

            if not riff_type:
                continue

            props = infer_component_props(visual, riff_type)

            layout = extract_layout(visual.get("layout", {}))
            style = extract_style(visual.get("style", {}))

            endpoint_name = generate_endpoint_name(page_name, v_title)
            data_source = RiffDataSource(
                endpoint_name=endpoint_name,
                description=(
                    f"Fetches data for {v_title} ({riff_type}). "
                    f"Columns: {', '.join(props.yAxis or [])}"
                ),
                params=[],
            )

            component = RiffComponent(
                id=visual.get("id"),
                type="Chart" if "Chart" in riff_type else ("Stat" if "Stat" in riff_type else "Other"),
                visual_type_source=v_type,
                props=props,
                dataSource=data_source,
                layout=layout,
                style=style,
                description=(
                    f"Derived from {v_type}. Displays {', '.join(props.yAxis or [])} "
                    f"by {props.xAxis or props.dataKey or 'Category'}"
                ),
            )

            riff_components.append(component)

        riff_pages.append(
            RiffPage(
                id=page_id,
                name=page_name,
                width=page.get("width"),
                height=page.get("height"),
                style=page_style,
                components=riff_components,
            )
        )

    app_name = analysis_result.get("filename", "My App").replace(".pbix", "")

    return RiffAppPlan(
        app_name=app_name,
        pages=riff_pages,
    )
