"""Compass tab for the parliamentary explorer."""

from __future__ import annotations

import datetime as _dt
import re
from typing import Dict, Tuple

import numpy as np
import pandas as pd

from analysis import config
import analysis.explorer_data as explorer_data
from analysis.tabs._rendering import px, st

PARTY_COLOURS = config.PARTY_COLOURS


def build_compass_tab(db_path: str, window_size: str) -> None:
    """Build the Politiek Kompas tab."""
    st.subheader("Politiek Kompas")
    st.markdown(
        "2D projectie van Kamerlid posities op basis van stemgedrag (PCA op SVD-vectoren)."
    )

    # Compass always uses annual windows regardless of the sidebar window_size setting.
    positions_by_window, axis_def = explorer_data.load_positions(db_path, "annual")
    if axis_def is None:
        axis_def = {}
    if not positions_by_window:
        st.warning(
            "Geen positiedata beschikbaar. Controleer of de pipeline is gedraaid."
        )
        return

    party_map = explorer_data.load_party_map(db_path)
    active_mps = explorer_data.load_active_mps(db_path)

    _current_year = str(_dt.date.today().year)
    year_windows = sorted(
        w
        for w in positions_by_window
        if w != "current_parliament" and w != _current_year
    )
    has_current = "current_parliament" in positions_by_window
    windows = year_windows + (["current_parliament"] if has_current else [])

    _SPARSE_YEARS = {"2016", "2017", "2018"}
    _THRESHOLD = 0.65

    def _window_label(w: str) -> str:
        if w == "current_parliament":
            return "Huidig parlement"
        return w

    col1, col2 = st.columns([3, 1])
    with col2:
        window_idx = st.selectbox(
            "Jaar",
            options=windows,
            index=len(windows) - 1,
            format_func=_window_label,
        )
        level = st.radio(
            "Weergave",
            options=["Kamerleden", "Partijen"],
            index=0,
            horizontal=True,
        )
        min_mps = st.number_input(
            "Min. Kamerleden per partij",
            min_value=1,
            max_value=20,
            value=3,
            step=1,
            help="Partijen met minder dan dit aantal zetels worden niet weergegeven.",
        )

    pos = positions_by_window.get(window_idx, {})
    if not pos:
        st.info(f"Geen data voor venster {window_idx}")
        return

    if window_idx == "current_parliament":
        pos = {mp: xy for mp, xy in pos.items() if mp in active_mps}

    def _strip_paren(name: str) -> str:
        return re.sub(r"\s*\([^)]*\)", "", name).strip()

    deduped: Dict[str, Tuple[float, float]] = {}
    for name, (x, y) in pos.items():
        base = _strip_paren(name)
        if base in deduped:
            ox, oy = deduped[base]
            deduped[base] = ((ox + x) / 2, (oy + y) / 2)
        else:
            deduped[base] = (x, y)
    pos = deduped

    rows = []
    for name, (x, y) in pos.items():
        party = party_map.get(name) or party_map.get(_strip_paren(name), "Unknown")
        rows.append({"name": name, "x": x, "y": y, "party": party})

    df_pos = pd.DataFrame(rows)

    party_counts = df_pos[df_pos["party"] != "Unknown"]["party"].value_counts()
    valid_parties = set(party_counts[party_counts >= min_mps].index)
    df_pos = df_pos[df_pos["party"].isin(valid_parties)]

    if df_pos.empty:
        st.info("Geen partijen met genoeg Kamerleden voor dit venster.")
        return

    _raw_x = axis_def.get("x_label")
    _raw_y = axis_def.get("y_label")

    try:
        from analysis.axis_classifier import display_label_for_modal

        _x_label = display_label_for_modal(_raw_x, "x")
        _y_label = display_label_for_modal(_raw_y, "y")
    except Exception:
        from analysis.svd_labels import get_fallback_labels

        _x_fallback, _y_fallback = get_fallback_labels()
        _x_label = _raw_x or _x_fallback
        _y_label = _raw_y or _y_fallback

    if level == "Partijen":
        df_party = df_pos.groupby("party", as_index=False).agg(
            x=("x", "mean"), y=("y", "mean"), n=("name", "count")
        )
        df_party["name"] = df_party["party"]
        colour_map = {
            p: PARTY_COLOURS.get(p, "#9E9E9E") for p in df_party["party"].unique()
        }
        fig = px.scatter(
            df_party,
            x="x",
            y="y",
            color="party",
            text="party",
            hover_name="party",
            hover_data={"party": False, "x": ":.3f", "y": ":.3f", "n": True},
            color_discrete_map=colour_map,
            title=f"Politiek Kompas — {_window_label(window_idx)} (partijen)",
            labels={
                "x": _x_label,
                "y": _y_label,
                "n": "Kamerleden",
            },
        )
        fig.update_traces(textposition="top center", marker_size=14)
    else:
        colour_map = {
            p: PARTY_COLOURS.get(p, "#9E9E9E") for p in df_pos["party"].unique()
        }
        fig = px.scatter(
            df_pos,
            x="x",
            y="y",
            color="party",
            hover_name="name",
            hover_data={"party": True, "x": ":.3f", "y": ":.3f"},
            color_discrete_map=colour_map,
            title=f"Politiek Kompas — {_window_label(window_idx)}",
            labels={"x": _x_label, "y": _y_label},
        )

    fig.update_layout(
        height=600,
        legend_title_text="Partij",
        xaxis={"range": [-1, 1]},
        yaxis={"range": [-0.6, 0.6]},
    )
    with col1:
        st.plotly_chart(fig, use_container_width=True)
        _x_interp = axis_def.get("x_interpretation", {}).get(window_idx, "")
        if (
            _x_interp
            and axis_def.get("x_quality", {}).get(window_idx, 1.0) < _THRESHOLD
        ):
            st.caption(_x_interp)

        with st.expander("Overton Window Context"):
            st.markdown(
                "Het SVD-kompas visualiseert de dynamiek achter de "
                "**verbreding van het Overton-venster** in de Tweede Kamer.\n\n"
                "Centristische steun voor rechtse moties steeg van 25% naar 51% na 2024, "
                "terwijl steun voor linkse moties gelijk bleef. Het venster verschoof — "
                "meer rechtse standpunten werden acceptabel.\n\n"
                "Maar: **centristische partijen** (D66, CDA, CU, NSC) zijn op **beide assen naar"
                " links** verschoven, terwijl rechtse partijen stabiel bleven. Dit patroon"
                ' van "acceptatie zonder conversie" betekent dat rechtse partijen mildere'
                " moties gingen indienen, en centristen daardoor vaker konden meestemmen — "
                "zonder dat ze ideologisch naar rechts opschoven.\n\n"
                "[Lees de volledige analyse](../reports/overton_window/overton_window.qmd)\n\n"
                "Probeer de **Stemwijzer-quiz** om te zien welke MP bij jouw standpunten past."
            )
        st.markdown("---")
        st.markdown(
            "**Stemdiscipline analyse:** De Rice-index meet hoe eensgezind partijen stemmen "
            "tijdens hoofdelijke stemmingen. Een score van 100% betekent dat alle MPs van "
            "een partij hetzelfde stemden; 50% wijst op een gelijke splitsing binnen de partij. "
            "Partijen met hoge discipline (>95%) zoals PVV en SGP stemmen als een blok, wat "
            "wijst op sterke partijdiscipline en homogene membership. Lagere discipline (<85%) "
            "bij partijen als PvdA of SP kan duiden op interne factiestrijd, gewetensvragen "
            "bij ethische thema's, of een brede ideologische koers die ruimte laat voor "
            "afwijkende meningen. De discipline varieert ook per onderwerp — ethische kwesties "
            "tonen vaak meer interne verschillen dan economische thema's."
        )
