"""Circular cycle diagram renderer."""

from __future__ import annotations

import math

from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
from pptx.slide import Slide
from pptx.util import Inches, Pt

from pptx_mcp.engine.diagrams._common import (
    add_freeform_connector,
    add_textbox,
    brand_color_at,
    text_color_for_shape,
)
from pptx_mcp.engine.layouts import _add_slide_title
from pptx_mcp.engine.shapes import parse_hex_color
from pptx_mcp.models.brand import BrandConfig
from pptx_mcp.models.slides import SlideDefinition


def _arrow_midpoint(
    slide: Slide,
    x1: float,
    y1: float,
    x2: float,
    y2: float,
    color: str,
    brand: BrandConfig,
) -> None:
    """Draw a small arrow shape at the midpoint between two nodes, rotated to face the target."""
    mx = (x1 + x2) / 2
    my = (y1 + y2) / 2
    angle_deg = math.degrees(math.atan2(y2 - y1, x2 - x1))

    arrow_size = 0.25
    arrow = slide.shapes.add_shape(
        MSO_SHAPE.RIGHT_ARROW,
        Inches(mx - arrow_size / 2),
        Inches(my - arrow_size / 2),
        Inches(arrow_size),
        Inches(arrow_size),
    )
    arrow.fill.solid()
    arrow.fill.fore_color.rgb = parse_hex_color(color)
    arrow.line.fill.background()
    arrow.rotation = angle_deg


def render_cycle_slide(
    slide: Slide,
    definition: SlideDefinition,
    brand: BrandConfig,
    slide_width: float,
) -> None:
    """Render a circular cycle diagram with nodes and directional arrows."""
    _add_slide_title(slide, definition.title, brand, slide_width)

    steps = definition.cycle_steps or []
    if not steps:
        return

    n = len(steps)
    sp = brand.spacing
    content_w = slide_width - sp.margin_left - sp.margin_right

    cx = sp.margin_left + content_w / 2
    cy = sp.content_top + 2.6
    radius = 2.2
    node_r = 0.5

    # Compute positions for each node
    positions: list[tuple[float, float]] = []
    for i in range(n):
        angle = 2 * math.pi * i / n - math.pi / 2  # start from top
        px = cx + radius * math.cos(angle)
        py = cy + radius * math.sin(angle)
        positions.append((px, py))

    # --- arrows between consecutive nodes ---
    for i in range(n):
        j = (i + 1) % n
        x1, y1 = positions[i]
        x2, y2 = positions[j]
        # Points on the edges of the nodes
        angle_to_next = math.atan2(y2 - y1, x2 - x1)
        ex1 = x1 + node_r * math.cos(angle_to_next)
        ey1 = y1 + node_r * math.sin(angle_to_next)
        ex2 = x2 - node_r * math.cos(angle_to_next)
        ey2 = y2 - node_r * math.sin(angle_to_next)

        _arrow_midpoint(
            slide, ex1, ey1, ex2, ey2,
            color=brand.colors.text_secondary,
            brand=brand,
        )

    # --- node circles ---
    for i, step in enumerate(steps):
        px, py = positions[i]
        color_hex = step.color or brand_color_at(brand, i)

        node = slide.shapes.add_shape(
            MSO_SHAPE.OVAL,
            Inches(px - node_r), Inches(py - node_r),
            Inches(node_r * 2), Inches(node_r * 2),
        )
        node.fill.solid()
        node.fill.fore_color.rgb = parse_hex_color(color_hex)
        node.line.fill.background()

        tf = node.text_frame
        tf.word_wrap = True
        tf.vertical_anchor = MSO_ANCHOR.MIDDLE
        p = tf.paragraphs[0]
        p.text = step.label
        p.alignment = PP_ALIGN.CENTER
        run = p.runs[0]
        run.font.name = brand.fonts.body_font
        run.font.size = Pt(10)
        run.font.bold = True
        run.font.color.rgb = parse_hex_color(text_color_for_shape(color_hex, brand))

        # Optional description outside the circle
        if step.description:
            desc_w = node_r * 2.4
            # Place description below for top-half nodes, above for bottom-half
            angle = 2 * math.pi * i / n - math.pi / 2
            if math.sin(angle) >= 0:
                desc_y = py + node_r + 0.05
            else:
                desc_y = py - node_r - 0.32
            add_textbox(
                slide,
                px - desc_w / 2,
                desc_y,
                desc_w,
                0.3,
                text=step.description,
                font_name=brand.fonts.body_font,
                font_size=10,
                color=brand.colors.text_secondary,
            )
