"""
Charting components powered by ApexCharts.
"""
import json
import logging
from typing import Optional, Union

from .base import Component

logger = logging.getLogger(__name__)


class ChartHeader(Component):
    """Header component for charts with optional filtering capability."""
    def __init__(
        self,
        filter_component: Optional[Component] = None,
        title: str = "",
        subtitle: str = "",
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.filter_component = filter_component
        self.title = title
        self.subtitle = subtitle

    def render_html(self, **kwargs) -> str:
        title_html = (
            f"<div id='{self.uid}_title' class='text-xl font-semibold'>{self.title}</div>"
            if self.title
            else ""
        )
        subtitle_html = (
            f"<div id='{self.uid}_subtitle' class='text-sm text-gray-500'>{self.subtitle}</div>"
            if self.subtitle
            else ""
        )

        filter_html = ""
        if self.filter_component:
            # Override the filter component to fire our special Alpine event instead of submitting via HTMX
            self.filter_component.alpine_event = "chart-filter-submit"

            filter_html = f"""
            <details id='{self.uid}_filter' class="dropdown dropdown-end">
                <summary class="btn btn-square dropdown-toggle btn-primary btn-sm">
                    {{% heroicon_mini "funnel" %}}
                </summary>
                <div class="card w-64 my-1.5 card-body shadow dropdown-content border border-base-300 rounded-box z-[2] bg-base-100">
                    {self.filter_component.render(**kwargs)}
                </div>
            </details>
            """

        if not (title_html or subtitle_html or filter_html):
            return ""

        return f"""
        <div id='{self.uid}' class='flex justify-between items-center relative my-2 {self.classes}'>
            <div> {title_html} {subtitle_html} </div>
            <div class="flex items-center gap-2">
                {filter_html}
            </div>
        </div>
        """


class Chart(Component):
    """
    A generic component for rendering charts using the ApexCharts library.
    It expects a URL to fetch chart data from. The endpoint should return
    JSON data compatible with ApexCharts options (e.g., {"series": [...], "xaxis": {...}}).
    """

    def __init__(
        self,
        type: str = "line",
        height: Union[int, str] = 350,
        options: Optional[dict] = None,
        filter_component: Optional[Component] = None,
        title: str = "",
        subtitle: str = "",
        **kwargs,
    ):
        super().__init__(**kwargs)
        header_uid = f"{self.uid}_header" if self.uid else "header"
        header = ChartHeader(
            filter_component=filter_component,
            title=title,
            subtitle=subtitle,
            uid=header_uid,
        )
        self.type = type
        self.height = height
        self.options = options or {}
        self.header = header

    def render_html(self, **kwargs) -> str:
        chart_id = f"chart_{self.uid}"

        # Base options to ensure the chart renders properly
        base_options = {
            "chart": {
                "type": self.type,
                "height": self.height,
            },
            "series": [],
            "noData": {"text": "No data to display"},
        }

        # Merge custom options provided during instantiation
        for k, v in self.options.items():
            if k == "chart" and isinstance(v, dict):
                base_options["chart"].update(v)
            else:
                base_options[k] = v

        options_json = json.dumps(base_options)

        header_html = self.header.render(**kwargs)

        url_js = f"'{self.url}'" if self.url else "window.location.pathname"

        return f"""
            <div id="{self.uid}" class="w-full {self.classes}" x-data="{{
                chart: null,
                init() {{
                    const optionsEl = document.getElementById('{chart_id}_options');
                    if (!optionsEl) return;
                    
                    const options = JSON.parse(optionsEl.textContent);
                    
                    // Attach click handler for data points
                    if (!options.chart) options.chart = {{}};
                    if (!options.chart.events) options.chart.events = {{}};
                    
                    options.chart.events.dataPointSelection = (event, chartContext, config) => {{
                        const seriesIndex = config.seriesIndex;
                        const dataPointIndex = config.dataPointIndex;

                        // Extract the exact data point that was clicked from the updated internal options
                        const dataPoint = chartContext.w.config.series[seriesIndex].data[dataPointIndex];

                        // If the data point has a URL attached, trigger HTMX navigation
                        if (dataPoint && dataPoint.url) {{
                            htmx.ajax('GET', dataPoint.url, {{ target: '#app-layout', swap: 'outerHTML' }});
                        }}
                    }};

                    // Refetch data when the chart is zoomed or panned (debounced)
                    let zoomTimeout = null;
                    options.chart.events.zoomed = (chartContext, {{ xaxis }}) => {{
                        if (xaxis && xaxis.min && xaxis.max) {{
                            clearTimeout(zoomTimeout);
                            zoomTimeout = setTimeout(() => {{
                                const params = new URLSearchParams(window.location.search);
                                params.set('range_min', new Date(xaxis.min).toISOString());
                                params.set('range_max', new Date(xaxis.max).toISOString());
                                this.loadData(params);
                            }}, 1000);
                        }}
                    }};

                    this.$nextTick(() => {{
                        this.chart = new ApexCharts(this.$refs.chart, options);
                        this.chart.render();
                    }});

                }},
                destroy() {{
                    if (this.chart) {{
                        this.chart.destroy();
                        this.chart = null;
                    }}
                }},
                loadData(params = null) {{
                        const baseUrl = {url_js};
                        const fetchUrl = new window.URL(baseUrl, window.location.origin);
                        
                        if (params) {{
                            // Use params from the form submission
                            params.forEach((value, key) => {{
                                if (value) {{
                                    fetchUrl.searchParams.append(key, value);
                                }}
                            }});
                        }} else {{
                            // Fallback to reading from the current URL on initial load
                            const currentParams = new window.URLSearchParams(window.location.search);
                            currentParams.forEach((value, key) => {{
                                if (value) {{
                                    fetchUrl.searchParams.append(key, value);
                                }}
                            }});
                        }}

                        fetch(fetchUrl, {{
                            headers: {{
                                'Accept': 'application/json'
                            }}
                        }})
                            .then(res => res.json())
                            .then(data => {{
                                if (this.chart) {{
                                    this.chart.updateOptions(data);
                                }}
                            }})
                            .catch(() => {{}});
                }}
            }}" @chart-filter-submit.window="loadData(new window.URLSearchParams(new window.FormData($event.detail.form)))" x-init="loadData()">
                {header_html}
                <script type="application/json" id="{chart_id}_options">
                    {options_json}
                </script>
                <div id="{chart_id}" x-ref="chart"></div>
            </div>
        """
