"""3D scene and 2D effect primitives for unified OOXML styling.

Provides camera, lighting, shadow, glow, reflection, and bevel definitions
for DrawingML visual effects.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from typing import Literal, Optional

from tokenmoulds.ir.colors import ColorRef


__all__ = [
    "CameraPreset",
    "LightRigPreset",
    "LightDirection",
    "Rotation3D",
    "Camera3D",
    "LightRig3D",
    "Scene3D",
    "Shadow",
    "Glow",
    "Reflection",
    "SoftEdge",
    "Bevel3D",
    "Effects",
]


# =============================================================================
# 3D SCENE PRIMITIVES
# =============================================================================


class CameraPreset(Enum):
    """Camera preset types for 3D scenes.

    GUI offers ~20 presets. But rotation can be any angle.
    """

    ORTHOGRAPHIC_FRONT = "orthographicFront"
    ISOMETRIC_LEFT_DOWN = "isometricLeftDown"
    ISOMETRIC_RIGHT_DOWN = "isometricRightDown"
    ISOMETRIC_LEFT_UP = "isometricLeftUp"
    ISOMETRIC_RIGHT_UP = "isometricRightUp"
    ISOMETRIC_BOTTOM_DOWN = "isometricBottomDown"
    ISOMETRIC_BOTTOM_UP = "isometricBottomUp"
    ISOMETRIC_TOP_DOWN = "isometricTopDown"
    ISOMETRIC_TOP_UP = "isometricTopUp"
    ISOMETRIC_OFF_AXIS_1_LEFT = "isometricOffAxis1Left"
    ISOMETRIC_OFF_AXIS_1_RIGHT = "isometricOffAxis1Right"
    ISOMETRIC_OFF_AXIS_1_TOP = "isometricOffAxis1Top"
    ISOMETRIC_OFF_AXIS_2_LEFT = "isometricOffAxis2Left"
    ISOMETRIC_OFF_AXIS_2_RIGHT = "isometricOffAxis2Right"
    ISOMETRIC_OFF_AXIS_2_TOP = "isometricOffAxis2Top"
    ISOMETRIC_OFF_AXIS_3_LEFT = "isometricOffAxis3Left"
    ISOMETRIC_OFF_AXIS_3_RIGHT = "isometricOffAxis3Right"
    ISOMETRIC_OFF_AXIS_3_BOTTOM = "isometricOffAxis3Bottom"
    ISOMETRIC_OFF_AXIS_4 = "isometricOffAxis4"
    OBLIQUE_TOP_LEFT = "obliqueTopLeft"
    OBLIQUE_TOP_RIGHT = "obliqueTopRight"
    OBLIQUE_BOTTOM_LEFT = "obliqueBottomLeft"
    OBLIQUE_BOTTOM_RIGHT = "obliqueBottomRight"
    PERSPECTIVE_FRONT = "perspectiveFront"
    PERSPECTIVE_LEFT = "perspectiveLeft"
    PERSPECTIVE_RIGHT = "perspectiveRight"
    PERSPECTIVE_ABOVE = "perspectiveAbove"
    PERSPECTIVE_BELOW = "perspectiveBelow"
    PERSPECTIVE_ABOVE_LEFT_FACING = "perspectiveAboveLeftFacing"
    PERSPECTIVE_ABOVE_RIGHT_FACING = "perspectiveAboveRightFacing"
    PERSPECTIVE_CONTRASTING_LEFT_FACING = "perspectiveContrastingLeftFacing"
    PERSPECTIVE_CONTRASTING_RIGHT_FACING = "perspectiveContrastingRightFacing"
    PERSPECTIVE_HEROIC_LEFT_FACING = "perspectiveHeroicLeftFacing"
    PERSPECTIVE_HEROIC_RIGHT_FACING = "perspectiveHeroicRightFacing"
    PERSPECTIVE_HEROIC_EXTREME_LEFT_FACING = "perspectiveHeroicExtremeLeftFacing"
    PERSPECTIVE_HEROIC_EXTREME_RIGHT_FACING = "perspectiveHeroicExtremeRightFacing"
    PERSPECTIVE_RELAXED = "perspectiveRelaxed"
    PERSPECTIVE_RELAXED_MODERATELY = "perspectiveRelaxedModerately"


class LightRigPreset(Enum):
    """Light rig preset types.

    GUI offers ~20 presets. But rotation can be any angle.
    """

    LEGACY_FLAT_1 = "legacyFlat1"
    LEGACY_FLAT_2 = "legacyFlat2"
    LEGACY_FLAT_3 = "legacyFlat3"
    LEGACY_FLAT_4 = "legacyFlat4"
    LEGACY_NORMAL_1 = "legacyNormal1"
    LEGACY_NORMAL_2 = "legacyNormal2"
    LEGACY_NORMAL_3 = "legacyNormal3"
    LEGACY_NORMAL_4 = "legacyNormal4"
    LEGACY_HARSH_1 = "legacyHarsh1"
    LEGACY_HARSH_2 = "legacyHarsh2"
    LEGACY_HARSH_3 = "legacyHarsh3"
    LEGACY_HARSH_4 = "legacyHarsh4"
    THREE_PT = "threePt"
    BALANCED = "balanced"
    SOFT = "soft"
    HARSH = "harsh"
    FLOOD = "flood"
    CONTRASTING = "contrasting"
    MORNING = "morning"
    SUNRISE = "sunrise"
    SUNSET = "sunset"
    CHILLY = "chilly"
    FREEZING = "freezing"
    FLAT = "flat"
    TWO_PT = "twoPt"
    GLOW = "glow"
    BRIGHT_ROOM = "brightRoom"


class LightDirection(Enum):
    """Direction the light rig faces."""

    TOP = "t"
    BOTTOM = "b"
    LEFT = "l"
    RIGHT = "r"
    TOP_LEFT = "tl"
    TOP_RIGHT = "tr"
    BOTTOM_LEFT = "bl"
    BOTTOM_RIGHT = "br"


@dataclass
class Rotation3D:
    """3D rotation angles.

    Values in OOXML units: 60000ths of a degree.
    Range: 0-21600000 (0° to 360°)
    """

    lat: int = 0  # Latitude (tilt)
    lon: int = 0  # Longitude (pan)
    rev: int = 0  # Revolution (roll)

    DEGREES_TO_OOXML = 60000

    @classmethod
    def from_degrees(cls, lat: float = 0, lon: float = 0, rev: float = 0) -> Rotation3D:
        """Create a Rotation3D from angles specified in degrees."""
        return cls(
            lat=int(lat * cls.DEGREES_TO_OOXML),
            lon=int(lon * cls.DEGREES_TO_OOXML),
            rev=int(rev * cls.DEGREES_TO_OOXML),
        )


@dataclass
class Camera3D:
    """3D camera definition for scene.

    The camera determines the viewing angle for 3D effects.
    GUI offers presets; this allows any rotation.

    Example:
        >>> # Custom 20° tilt (not a GUI preset!)
        >>> Camera3D(
        ...     preset=CameraPreset.ORTHOGRAPHIC_FRONT,
        ...     rotation=Rotation3D.from_degrees(lat=20),
        ... )
    """

    preset: CameraPreset = CameraPreset.ORTHOGRAPHIC_FRONT
    rotation: Optional[Rotation3D] = None
    # Field of view for perspective cameras (in degrees)
    fov: Optional[float] = None
    # Zoom factor (percentage, 100 = normal)
    zoom: Optional[int] = None


@dataclass
class LightRig3D:
    """3D lighting rig definition.

    Controls how light falls on 3D shapes.
    GUI offers presets; this allows any rotation.

    Example:
        >>> # Three-point lighting with 30° rotation
        >>> LightRig3D(
        ...     preset=LightRigPreset.THREE_PT,
        ...     direction=LightDirection.TOP,
        ...     rotation=Rotation3D.from_degrees(rev=30),
        ... )
    """

    preset: LightRigPreset = LightRigPreset.THREE_PT
    direction: LightDirection = LightDirection.TOP
    rotation: Optional[Rotation3D] = None


@dataclass
class Scene3D:
    """Complete 3D scene definition.

    Combines camera angle and lighting for 3D effects.
    This is the hidden power feature - GUI only offers presets,
    but XML allows any rotation angles.

    Example:
        >>> # Dramatic 3D scene (not possible in GUI!)
        >>> Scene3D(
        ...     camera=Camera3D(
        ...         preset=CameraPreset.ORTHOGRAPHIC_FRONT,
        ...         rotation=Rotation3D.from_degrees(lat=20),
        ...     ),
        ...     light_rig=LightRig3D(
        ...         preset=LightRigPreset.THREE_PT,
        ...         direction=LightDirection.TOP,
        ...         rotation=Rotation3D.from_degrees(rev=30),
        ...     ),
        ... )
    """

    camera: Camera3D = field(default_factory=Camera3D)
    light_rig: LightRig3D = field(default_factory=LightRig3D)


# =============================================================================
# EFFECT PRIMITIVES
# =============================================================================


@dataclass
class Shadow:
    """Shadow effect (outer or inner).

    All distance/size values are in EMU.
    Direction is in 60000ths of a degree (5400000 = 90° = below).

    Values should come from DTCG tokens, not hardcoded defaults.
    """

    blur_radius: int  # EMU
    distance: int  # EMU
    direction: int  # 60000ths of a degree (5400000 = 90° = below)
    color: ColorRef
    rotate_with_shape: bool = False
    inner: bool = False  # True for inner shadow

    DEGREES_TO_OOXML = 60000


@dataclass
class Glow:
    """Glow effect around a shape.

    Values should come from DTCG tokens, not hardcoded defaults.
    """

    radius: int  # EMU
    color: ColorRef


@dataclass
class Reflection:
    """Reflection effect below a shape.

    Creates a mirror reflection with gradient fade.
    This is a hidden feature - GUI offers only a few presets,
    but XML allows full control over all parameters.

    Values in OOXML units:
    - blur_radius: EMU
    - distance: EMU (gap between shape and reflection)
    - direction: 60000ths of a degree (5400000 = 90° = below)
    - fade_direction: 60000ths of a degree (90° = fade up)
    - start_alpha/end_alpha: 0-100000 (percentage * 1000)
    - scale_x/scale_y: -100000 to 100000 (-100% to 100%)

    Values should come from DTCG tokens, not hardcoded defaults.
    """

    blur_radius: int  # EMU
    distance: int  # EMU - gap between shape and reflection
    direction: int  # 60000ths of a degree (5400000 = 90° = below)
    start_alpha: int  # 0-100000 (percentage * 1000)
    end_alpha: int  # 0-100000
    fade_direction: int  # 60000ths of a degree
    scale_x: int  # -100000 to 100000 (-100% to 100%)
    scale_y: int  # -100000 to 100000 (negative = flip)
    align: Literal["b", "t", "l", "r", "ctr"]
    rotate_with_shape: bool

    DEGREES_TO_OOXML = 60000


@dataclass
class SoftEdge:
    """Soft edge (feather) effect.

    Blurs the edges of a shape for a softer appearance.
    Values should come from DTCG tokens, not hardcoded defaults.
    """

    radius: int  # EMU


@dataclass
class Bevel3D:
    """3D bevel effect for shapes.

    Creates beveled/chamfered edges on shapes.
    GUI offers presets; XML allows custom dimensions.

    Bevel presets: relaxedInset, circle, slope, cross, angle,
                   softRound, convex, coolSlant, divot, riblet,
                   hardEdge, artDeco

    Values should come from DTCG tokens, not hardcoded defaults.
    """

    preset: str
    width: int  # EMU
    height: int  # EMU


@dataclass
class Effects:
    """Collection of effects for a shape or element.

    OOXML allows multiple effects per element, applied in order.
    This is hidden from the GUI which typically only shows one
    effect at a time, but XML supports full compound effects.

    Shadow, glow, and reflection values should come from DTCG tokens.

    Example (compound effects - not possible in GUI!):
        >>> Effects(
        ...     shadow=Shadow(...),
        ...     glow=Glow(...),
        ...     reflection=Reflection.floor_reflection(),
        ...     soft_edge=SoftEdge.from_points(2),
        ... )
    """

    shadow: Optional[Shadow] = None
    glow: Optional[Glow] = None
    reflection: Optional[Reflection] = None
    soft_edge: Optional[SoftEdge] = None
    inner_shadow: Optional[Shadow] = None  # Second shadow (inner)
    bevel_top: Optional[Bevel3D] = None
    bevel_bottom: Optional[Bevel3D] = None
    scene_3d: Optional[Scene3D] = None  # 3D scene for this element
