"""Fractal drawings: trees, Koch snowflake, Sierpinski, dragon curve."""

import math
import random
import turtle

from turtle_mcp.utils import get_palette, palette_color, draw_filled_circle


# ---------------------------------------------------------------------------
# Fractal tree
# ---------------------------------------------------------------------------

_SEASON_COLORS = {
    "spring": {"trunk": "#5C4033", "leaves": ["#90EE90", "#00FF7F", "#ADFF2F", "#FFB7C5"]},
    "summer": {"trunk": "#5C4033", "leaves": ["#228B22", "#006400", "#32CD32", "#2E8B57"]},
    "autumn": {"trunk": "#5C4033", "leaves": ["#FF4500", "#FF8C00", "#FFD700", "#8B0000"]},
    "winter": {"trunk": "#696969", "leaves": []},
}


def draw_fractal_tree(t: turtle.RawTurtle, screen: turtle.TurtleScreen,
                      depth: int = 8, branch_angle: float = 25.0,
                      season: str = "summer", wind: float = 0.0) -> None:
    """Draw a recursive fractal tree with seasonal colours."""
    sc = _SEASON_COLORS.get(season, _SEASON_COLORS["summer"])
    rng = random.Random(42)  # deterministic

    t.penup()
    t.goto(0, -300)
    t.setheading(90)
    t.pendown()

    def _branch(length: float, d: int) -> None:
        if d == 0 or length < 2:
            if sc["leaves"]:
                leaf_color = rng.choice(sc["leaves"])
                draw_filled_circle(t, t.xcor(), t.ycor(), 4, leaf_color)
            return

        # Trunk / branch
        width = max(1, d)
        t.pensize(width)
        t.pencolor(sc["trunk"])
        t.forward(length)

        # Right branch
        t.right(branch_angle - wind + rng.uniform(-5, 5))
        _branch(length * 0.7, d - 1)

        # Left branch
        t.left(2 * branch_angle - wind * 0.5)
        _branch(length * 0.7, d - 1)

        # Return
        t.right(branch_angle + wind * 0.5 - rng.uniform(-5, 5))
        t.backward(length)

    _branch(120, depth)


# ---------------------------------------------------------------------------
# Koch snowflake
# ---------------------------------------------------------------------------

def draw_koch_snowflake(t: turtle.RawTurtle, screen: turtle.TurtleScreen,
                        depth: int = 4, size: float = 300.0,
                        fill_color: str | None = None,
                        palette: str | None = None) -> None:
    """Draw a Koch curve snowflake."""
    colors = get_palette(palette)
    t.pencolor(colors[0])
    t.pensize(2)

    # Centre the triangle
    t.penup()
    t.goto(-size / 2, size * math.sqrt(3) / 6)
    t.pendown()
    t.setheading(0)

    if fill_color:
        t.fillcolor(fill_color)
        t.begin_fill()

    for _ in range(3):
        _koch_segment(t, size, depth, colors)
        t.right(120)

    if fill_color:
        t.end_fill()


def _koch_segment(t: turtle.RawTurtle, length: float, depth: int,
                  colors: list[str]) -> None:
    if depth == 0:
        t.pencolor(palette_color(colors, int(t.heading()) % len(colors)))
        t.forward(length)
        return
    seg = length / 3
    _koch_segment(t, seg, depth - 1, colors)
    t.left(60)
    _koch_segment(t, seg, depth - 1, colors)
    t.right(120)
    _koch_segment(t, seg, depth - 1, colors)
    t.left(60)
    _koch_segment(t, seg, depth - 1, colors)


# ---------------------------------------------------------------------------
# Sierpinski triangle
# ---------------------------------------------------------------------------

def draw_sierpinski(t: turtle.RawTurtle, screen: turtle.TurtleScreen,
                    depth: int = 5, size: float = 400.0,
                    palette: str | None = None) -> None:
    """Draw a Sierpinski triangle fractal."""
    colors = get_palette(palette)
    t.pensize(1)

    # Centre the triangle
    h = size * math.sqrt(3) / 2
    ax, ay = -size / 2, -h / 3
    bx, by = size / 2, -h / 3
    cx, cy = 0, 2 * h / 3

    _sierpinski(t, ax, ay, bx, by, cx, cy, depth, colors, 0)


def _sierpinski(t: turtle.RawTurtle,
                ax: float, ay: float,
                bx: float, by: float,
                cx: float, cy: float,
                depth: int, colors: list[str], level: int) -> None:
    if depth == 0:
        color = palette_color(colors, level)
        t.pencolor(color)
        t.fillcolor(color)
        t.penup()
        t.goto(ax, ay)
        t.pendown()
        t.begin_fill()
        t.goto(bx, by)
        t.goto(cx, cy)
        t.goto(ax, ay)
        t.end_fill()
        return

    # Midpoints
    mx_ab = (ax + bx) / 2
    my_ab = (ay + by) / 2
    mx_bc = (bx + cx) / 2
    my_bc = (by + cy) / 2
    mx_ca = (cx + ax) / 2
    my_ca = (cy + ay) / 2

    _sierpinski(t, ax, ay, mx_ab, my_ab, mx_ca, my_ca, depth - 1, colors, level + 1)
    _sierpinski(t, mx_ab, my_ab, bx, by, mx_bc, my_bc, depth - 1, colors, level + 1)
    _sierpinski(t, mx_ca, my_ca, mx_bc, my_bc, cx, cy, depth - 1, colors, level + 1)


# ---------------------------------------------------------------------------
# Dragon curve
# ---------------------------------------------------------------------------

def draw_dragon_curve(t: turtle.RawTurtle, screen: turtle.TurtleScreen,
                      iterations: int = 12, segment_length: float = 5.0,
                      palette: str | None = None) -> None:
    """Draw a Heighway dragon curve."""
    colors = get_palette(palette)

    # Build sequence of turns: R=right, L=left
    sequence = _dragon_sequence(iterations)

    t.pensize(2)
    t.penup()
    t.goto(-100, 0)
    t.pendown()
    t.setheading(0)

    total = len(sequence) + 1
    for i, turn in enumerate(sequence):
        t.pencolor(palette_color(colors, i * len(colors) // total))
        t.forward(segment_length)
        if turn == "R":
            t.right(90)
        else:
            t.left(90)
    t.forward(segment_length)


def _dragon_sequence(n: int) -> list[str]:
    """Generate the turn sequence for a dragon curve of *n* iterations."""
    seq: list[str] = []
    for _ in range(n):
        # Insert R in the middle, then append reversed/flipped copy
        rev = ["L" if c == "R" else "R" for c in reversed(seq)]
        seq = seq + ["R"] + rev
    return seq
