"""
Generate all Tufte visualization examples using plotnine/dufte.
Principles: Maximize Data-Ink, Enforce Comparisons, Integrate Text & Graphics.
"""

from pathlib import Path

import numpy as np
import pandas as pd
from plotnine import theme, element_blank, element_rect  # Neu hinzugefügt

# Wir gehen davon aus, dass dieses Modul lokal existiert, wie im Snippet angegeben.
from dufteplots.plots import (
    dot_dash_plot,
    layered_focus,
    range_frame,
    slopegraph,
    small_multiples,
    sparklines,
    time_series,
)

OUTPUT = Path("output")
OUTPUT.mkdir(exist_ok=True)
np.random.seed(42)


def generate_random_walk(start_price, n, volatility=0.02):
    """Hilfsfunktion für realistische Aktienkurse (Geometrische Brownsche Bewegung)"""
    returns = np.random.normal(loc=0.0005, scale=volatility, size=n)
    price_path = start_price * (1 + returns).cumprod()
    return price_path


def main():
    print("Generating Tufte visualizations with realistic data context...\n")

    # ---------------------------------------------------------
    # GLOBALE EINSTELLUNG: RAHMEN ENTFERNEN
    # ---------------------------------------------------------
    # Dieses Theme überschreibt den Standardrahmen von plotnine/matplotlib
    no_border = theme(
        plot_background=element_rect(color="white"),  # Weißer Rand statt schwarz
        panel_border=element_blank(),                 # Kein Rahmen um die Daten
        axis_line=element_blank()                     # Keine Achsenlinien (optional)
    )

    # ---------------------------------------------------------
    # 1. Range-Frame Plot (Boxplot Alternative)
    # ---------------------------------------------------------
    print("... creating Range-Frame Plot (Michelson Speed of Light Data Proxy)")

    n_meas = 40
    df_dist = pd.DataFrame(
        {
            "Experiment": np.repeat(
                ["Expt 1 (Winter)", "Expt 2 (Spring)", "Expt 3 (Summer)"], n_meas
            ),
            "Velocity_Deviation": np.concatenate(
                [
                    np.random.normal(850, 40, n_meas),
                    np.random.normal(790, 25, n_meas),
                    np.random.normal(740, 15, n_meas),
                ]
            ),
        }
    )

    df_dist["Experiment"] = pd.Categorical(
        df_dist["Experiment"],
        categories=["Expt 1 (Winter)", "Expt 2 (Spring)", "Expt 3 (Summer)"],
        ordered=True,
    )

    # Plot erstellen + Rahmen entfernen
    p1 = range_frame(
        df_dist,
        "Experiment",
        "Velocity_Deviation",
        title="Speed of Light Measurements (Deviation from 299,000 km/s)",
    )
    p1 = p1 + no_border
    p1.save(OUTPUT / "1_range_frame.png", width=6, height=4, dpi=300)

    # ---------------------------------------------------------
    # 2. Dot-Dash Plot (Scatterplot mit Randverteilung)
    # ---------------------------------------------------------
    print("... creating Dot-Dash Plot (GDP vs Life Expectancy)")

    n_countries = 60
    gdp = np.exp(np.random.normal(9, 1, n_countries))
    life_exp = 45 + 5 * np.log(gdp / 100) + np.random.normal(0, 3, n_countries)
    life_exp = np.clip(life_exp, 50, 85)

    df_scatter = pd.DataFrame(
        {"GDP per Capita ($)": gdp, "Life Expectancy (Years)": life_exp}
    )

    p2 = dot_dash_plot(
        df_scatter,
        "GDP per Capita ($)",
        "Life Expectancy (Years)",
        title="Wealth & Health of Nations (Dot-Dash)",
    )
    p2 = p2 + no_border
    p2.save(OUTPUT / "2_dot_dash_plot.png", width=7, height=7, dpi=300)

    # ---------------------------------------------------------
    # 3. Slopegraph
    # ---------------------------------------------------------
    print("... creating Slopegraph (Government Receipts % GDP)")

    countries = ["Switzerland", "USA", "Germany", "UK", "France", "Sweden", "Japan", "Italy"]
    y1970 = [25.0, 31.0, 38.0, 42.0, 39.0, 46.0, 20.0, 30.0]
    y1979 = [31.0, 30.5, 43.0, 38.0, 44.0, 58.0, 26.0, 34.0]

    data_slope = []
    for country, v70, v79 in zip(countries, y1970, y1979):
        trend = "Increase" if v79 > v70 else "Decrease"
        data_slope.append({"Country": country, "Year": "1970", "Value": v70, "Trend": trend})
        data_slope.append({"Country": country, "Year": "1979", "Value": v79, "Trend": trend})

    df_slope = pd.DataFrame(data_slope)

    p3 = slopegraph(
        df_slope,
        "Country",
        "Year",
        "Value",
        title="Government Receipts as % of GDP (1970 vs 1979)",
    )
    p3 = p3 + no_border
    p3.save(OUTPUT / "3_slopegraph.png", width=6, height=7, dpi=300)

    # ---------------------------------------------------------
    # 4. Small Multiples
    # ---------------------------------------------------------
    print("... creating Small Multiples (Unemployment by Sector)")

    months = np.arange(1, 13)
    dfs = []
    dfs.append(pd.DataFrame({"Month": months, "Rate": 8 + 4 * np.cos((months - 1) / 12 * 2 * np.pi) + np.random.normal(0, 0.2, 12), "Sector": "Construction"}))
    dfs.append(pd.DataFrame({"Month": months, "Rate": 5 + 2 * np.exp(-(months - 1)) + np.random.normal(0, 0.2, 12), "Sector": "Retail"}))
    dfs.append(pd.DataFrame({"Month": months, "Rate": np.linspace(4, 2.5, 12) + np.random.normal(0, 0.1, 12), "Sector": "Tech"}))
    dfs.append(pd.DataFrame({"Month": months, "Rate": np.full(12, 3.5) + np.random.normal(0, 0.1, 12), "Sector": "Healthcare"}))

    df_multi = pd.concat(dfs)

    p4 = small_multiples(
        df_multi,
        "Month",
        "Rate",
        "Sector",
        ncol=2,
        title="Unemployment Rate % by Sector (Seasonal Patterns)",
        x_label="Month",
        y_label="Rate %",
    )
    p4 = p4 + no_border
    p4.save(OUTPUT / "4_small_multiples.png", width=8, height=6, dpi=300)

    # ---------------------------------------------------------
    # 5. Sparklines
    # ---------------------------------------------------------
    print("... creating Sparklines (Currency Trends)")

    n_days = 60
    time_points = np.arange(n_days)
    currencies = ["EUR/USD", "GBP/USD", "JPY/USD", "CHF/USD", "AUD/USD"]
    start_vals = [1.10, 1.30, 0.009, 1.05, 0.70]

    spark_data = []
    for curr, start in zip(currencies, start_vals):
        values = generate_random_walk(start, n_days, volatility=0.01)
        for t, v in zip(time_points, values):
            spark_data.append({"Currency": curr, "Day": t, "Rate": v})

    df_sparklines = pd.DataFrame(spark_data)

    p5 = sparklines(
        df_sparklines,
        "Currency",
        "Day",
        "Rate",
        title="60-Day Currency Trends (Sparklines)",
    )
    p5 = p5 + no_border
    p5.save(OUTPUT / "5_sparklines.png", width=5, height=6, dpi=300)

    # ---------------------------------------------------------
    # 6. Focus Sparkline / Spaghetti Plot with Highlight
    # ---------------------------------------------------------
    print("... creating Focus Sparkline")

    p6 = layered_focus(
        df_sparklines,
        "Day",
        "Rate",
        "Currency",
        "EUR/USD",
        title="Euro vs. Major Currencies (Contextual Layering)",
    )
    p6 = p6 + no_border
    p6.save(OUTPUT / "6_layered_focus.png", width=7, height=4, dpi=300)

    # ---------------------------------------------------------
    # 7. Time Series
    # ---------------------------------------------------------
    print("... creating Time Series (Keeling Curve Proxy)")

    years = np.arange(1960, 2025, 5)
    co2 = 315 + 1.5 * (years - 1960) + 0.01 * (years - 1960) ** 2
    df_line = pd.DataFrame({"Year": years, "CO2 (ppm)": co2})

    p7 = time_series(
        df_line,
        "Year",
        "CO2 (ppm)",
        title="Atmospheric CO2 Concentration (Mauna Loa Proxy)",
    )
    p7 = p7 + no_border
    p7.save(OUTPUT / "7_time_series.png", width=6, height=4, dpi=300)

    print(
        "\n✓ Fertig! Alle Tufte-Style Plots wurden ohne Rahmen generiert."
    )


if __name__ == "__main__":
    main()