"""Tests for expanded chart types: scatter, bubble, radar, doughnut, stacked, combo."""

from __future__ import annotations

from pptx import Presentation
from pptx.oxml.ns import qn

from pptx_mcp.engine.charts import add_chart_to_slide
from pptx_mcp.models.slides import (
    BubbleChartDataModel,
    BubbleDataPoint,
    BubbleDataSeries,
    ChartData,
    ChartDataSeries,
    ChartElement,
    ChartType,
    XyChartData,
    XyDataPoint,
    XyDataSeries,
)


def _blank_slide():
    prs = Presentation()
    return prs, prs.slides.add_slide(prs.slide_layouts[6])


# ------------------------------------------------------------------
# Scatter chart
# ------------------------------------------------------------------


def test_scatter_chart_xy_data(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.SCATTER,
        xy_data=XyChartData(
            series=[
                XyDataSeries(
                    name="Series 1",
                    points=[
                        XyDataPoint(x=1.0, y=2.0),
                        XyDataPoint(x=2.0, y=4.0),
                        XyDataPoint(x=3.0, y=3.0),
                    ],
                ),
            ]
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "scatter.pptx"
    prs.save(str(out))
    assert out.exists()
    assert len(slide.shapes) == 1


def test_scatter_chart_multiple_series(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.SCATTER,
        xy_data=XyChartData(
            series=[
                XyDataSeries(
                    name="A",
                    points=[XyDataPoint(x=1.0, y=1.0), XyDataPoint(x=2.0, y=2.0)],
                ),
                XyDataSeries(
                    name="B",
                    points=[XyDataPoint(x=1.5, y=3.0), XyDataPoint(x=2.5, y=1.0)],
                ),
            ]
        ),
        title="Multi-series Scatter",
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "scatter_multi.pptx"
    prs.save(str(out))
    assert out.exists()
    chart = slide.shapes[0].chart
    assert chart.has_title


def test_scatter_chart_fallback_category_data(tmp_path):
    """Scatter chart with CategoryChartData fallback (categories as x-values)."""
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.SCATTER,
        data=ChartData(
            categories=["1", "2", "3"],
            series=[ChartDataSeries(name="Vals", values=[10.0, 20.0, 15.0])],
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "scatter_fallback.pptx"
    prs.save(str(out))
    assert out.exists()


def test_scatter_chart_non_numeric_categories(tmp_path):
    """Non-numeric categories get sequential indices as x-values."""
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.SCATTER,
        data=ChartData(
            categories=["Jan", "Feb", "Mar"],
            series=[ChartDataSeries(name="Revenue", values=[100.0, 200.0, 150.0])],
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "scatter_non_numeric.pptx"
    prs.save(str(out))
    assert out.exists()


# ------------------------------------------------------------------
# Bubble chart
# ------------------------------------------------------------------


def test_bubble_chart_bubble_data(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.BUBBLE,
        bubble_data=BubbleChartDataModel(
            series=[
                BubbleDataSeries(
                    name="Products",
                    points=[
                        BubbleDataPoint(x=1.0, y=10.0, size=5.0),
                        BubbleDataPoint(x=2.0, y=20.0, size=10.0),
                        BubbleDataPoint(x=3.0, y=15.0, size=7.0),
                    ],
                ),
            ]
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "bubble.pptx"
    prs.save(str(out))
    assert out.exists()
    assert len(slide.shapes) == 1


def test_bubble_chart_fallback_category_data(tmp_path):
    """Bubble chart with CategoryChartData fallback (uniform bubble size)."""
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.BUBBLE,
        data=ChartData(
            categories=["1", "2", "3"],
            series=[ChartDataSeries(name="Vals", values=[5.0, 15.0, 10.0])],
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "bubble_fallback.pptx"
    prs.save(str(out))
    assert out.exists()


# ------------------------------------------------------------------
# Radar chart
# ------------------------------------------------------------------


def test_radar_chart(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.RADAR,
        data=ChartData(
            categories=["Speed", "Power", "Agility", "Defense", "Stamina"],
            series=[
                ChartDataSeries(name="Player A", values=[8.0, 6.0, 9.0, 5.0, 7.0]),
                ChartDataSeries(name="Player B", values=[6.0, 9.0, 5.0, 8.0, 6.0]),
            ],
        ),
        title="Radar Comparison",
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "radar.pptx"
    prs.save(str(out))
    assert out.exists()


# ------------------------------------------------------------------
# Doughnut chart
# ------------------------------------------------------------------


def test_doughnut_chart(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.DOUGHNUT,
        data=ChartData(
            categories=["A", "B", "C"],
            series=[ChartDataSeries(name="Share", values=[40.0, 35.0, 25.0])],
        ),
        title="Market Share",
    )
    add_chart_to_slide(slide, elem, chart_colors=["#FF0000", "#00FF00", "#0000FF"])
    out = tmp_path / "doughnut.pptx"
    prs.save(str(out))
    assert out.exists()


# ------------------------------------------------------------------
# Stacked bar chart
# ------------------------------------------------------------------


def test_stacked_bar_chart(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.STACKED_BAR,
        data=ChartData(
            categories=["Q1", "Q2", "Q3", "Q4"],
            series=[
                ChartDataSeries(name="Product A", values=[10.0, 20.0, 30.0, 25.0]),
                ChartDataSeries(name="Product B", values=[15.0, 10.0, 20.0, 30.0]),
            ],
        ),
        title="Stacked Bar",
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "stacked_bar.pptx"
    prs.save(str(out))
    assert out.exists()


# ------------------------------------------------------------------
# Stacked column chart
# ------------------------------------------------------------------


def test_stacked_column_chart(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.STACKED_COLUMN,
        data=ChartData(
            categories=["2021", "2022", "2023"],
            series=[
                ChartDataSeries(name="Revenue", values=[100.0, 150.0, 200.0]),
                ChartDataSeries(name="Expenses", values=[80.0, 120.0, 160.0]),
            ],
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "stacked_column.pptx"
    prs.save(str(out))
    assert out.exists()


# ------------------------------------------------------------------
# Combo chart
# ------------------------------------------------------------------


def test_combo_chart_basic(tmp_path):
    """Combo chart: first series as columns, last series as line overlay."""
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.COMBO,
        data=ChartData(
            categories=["Jan", "Feb", "Mar", "Apr"],
            series=[
                ChartDataSeries(name="Revenue", values=[100.0, 120.0, 110.0, 140.0]),
                ChartDataSeries(name="Trend", values=[105.0, 112.0, 115.0, 130.0]),
            ],
        ),
        title="Revenue vs Trend",
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "combo.pptx"
    prs.save(str(out))
    assert out.exists()

    # Verify XML has both barChart and lineChart
    chart = slide.shapes[0].chart
    plot_area = chart._chartSpace.find(qn("c:chart")).find(qn("c:plotArea"))
    assert plot_area.find(qn("c:barChart")) is not None
    assert plot_area.find(qn("c:lineChart")) is not None


def test_combo_chart_secondary_axis(tmp_path):
    """Combo chart with secondary Y-axis for the line series."""
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.COMBO,
        data=ChartData(
            categories=["Q1", "Q2", "Q3"],
            series=[
                ChartDataSeries(name="Units", values=[500.0, 600.0, 550.0]),
                ChartDataSeries(name="Margin %", values=[12.0, 15.0, 13.0]),
            ],
        ),
        secondary_axis=True,
        title="Units vs Margin",
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "combo_secondary.pptx"
    prs.save(str(out))
    assert out.exists()

    # Should have secondary value axis
    chart = slide.shapes[0].chart
    plot_area = chart._chartSpace.find(qn("c:chart")).find(qn("c:plotArea"))
    val_axes = plot_area.findall(qn("c:valAx"))
    assert len(val_axes) >= 2


def test_combo_chart_single_series_no_crash(tmp_path):
    """Combo with single series should not crash (no line conversion)."""
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.COMBO,
        data=ChartData(
            categories=["A", "B"],
            series=[ChartDataSeries(name="Only", values=[10.0, 20.0])],
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "combo_single.pptx"
    prs.save(str(out))
    assert out.exists()


# ------------------------------------------------------------------
# Edge cases
# ------------------------------------------------------------------


def test_scatter_single_data_point(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.SCATTER,
        xy_data=XyChartData(
            series=[
                XyDataSeries(name="One", points=[XyDataPoint(x=5.0, y=10.0)]),
            ]
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "scatter_single.pptx"
    prs.save(str(out))
    assert out.exists()


def test_bubble_single_data_point(tmp_path):
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.BUBBLE,
        bubble_data=BubbleChartDataModel(
            series=[
                BubbleDataSeries(
                    name="One",
                    points=[BubbleDataPoint(x=1.0, y=2.0, size=3.0)],
                ),
            ]
        ),
    )
    add_chart_to_slide(slide, elem)
    out = tmp_path / "bubble_single.pptx"
    prs.save(str(out))
    assert out.exists()


def test_chart_with_data_labels(tmp_path):
    """Data labels work on new chart types."""
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.STACKED_COLUMN,
        data=ChartData(
            categories=["A", "B"],
            series=[ChartDataSeries(name="S1", values=[10.0, 20.0])],
        ),
        show_data_labels=True,
    )
    add_chart_to_slide(slide, elem)
    chart = slide.shapes[0].chart
    # data_labels is always present; check show_value was set
    assert chart.series[0].data_labels.show_value is True


def test_chart_with_colors(tmp_path):
    """Series colors apply to new chart types."""
    prs, slide = _blank_slide()
    elem = ChartElement(
        chart_type=ChartType.RADAR,
        data=ChartData(
            categories=["A", "B", "C"],
            series=[
                ChartDataSeries(name="S1", values=[1.0, 2.0, 3.0]),
                ChartDataSeries(name="S2", values=[3.0, 2.0, 1.0]),
            ],
        ),
    )
    add_chart_to_slide(slide, elem, chart_colors=["#FF0000", "#0000FF"])
    out = tmp_path / "radar_colors.pptx"
    prs.save(str(out))
    assert out.exists()
