"""
Frame manager for pyPhotoAlbum

Manages decorative frames that can be applied to images:
- Loading frame assets (SVG/PNG)
- Rendering frames in OpenGL and PDF
- Frame categories (modern, vintage)
- Color override for SVG frames
"""

import io
import os
import re
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, field
from enum import Enum

from PIL import Image


class FrameCategory(Enum):
    """Categories for organizing frames"""

    MODERN = "modern"
    VINTAGE = "vintage"
    GEOMETRIC = "geometric"
    CUSTOM = "custom"


class FrameType(Enum):
    """How the frame is structured"""

    CORNERS = "corners"  # 4 corner pieces, rotated/mirrored
    FULL = "full"  # Complete frame as single image
    EDGES = "edges"  # Tileable edge pieces


@dataclass
class FrameDefinition:
    """Definition of a decorative frame"""

    name: str
    display_name: str
    category: FrameCategory
    frame_type: FrameType
    description: str = ""

    # Asset path (relative to frames/corners directory for CORNERS type)
    # For CORNERS type: single SVG that gets rotated for each corner
    asset_path: Optional[str] = None

    # Which corner the SVG asset is designed for: "tl", "tr", "br", "bl"
    # This determines how to flip for other corners
    asset_corner: str = "tl"

    # Whether the frame can be tinted with a custom color
    colorizable: bool = True

    # Default thickness as percentage of shorter image side
    default_thickness: float = 5.0

    # Cached textures for OpenGL rendering: key = (color, size) tuple
    _texture_cache: Dict[tuple, int] = field(default_factory=dict, repr=False)
    _image_cache: Dict[tuple, Image.Image] = field(default_factory=dict, repr=False)


class FrameManager:
    """
    Manages loading and rendering of decorative frames.

    Frames are stored in the frames/ directory with the following structure:
    frames/
        corners/
            floral_corner.svg
            ornate_corner.svg
        CREDITS.txt
    """

    def __init__(self):
        self.frames: Dict[str, FrameDefinition] = {}
        self._frames_dir = self._get_frames_directory()
        self._load_bundled_frames()

    def _get_frames_directory(self) -> Path:
        """Get the frames directory path"""
        app_dir = Path(__file__).parent
        return app_dir / "frames"

    def _load_bundled_frames(self):
        """Load bundled frame definitions"""
        # Modern frames (programmatic - no SVG assets)
        self._register_frame(
            FrameDefinition(
                name="simple_line",
                display_name="Simple Line",
                category=FrameCategory.MODERN,
                frame_type=FrameType.FULL,
                description="Clean single-line border",
                colorizable=True,
                default_thickness=2.0,
            )
        )

        self._register_frame(
            FrameDefinition(
                name="double_line",
                display_name="Double Line",
                category=FrameCategory.MODERN,
                frame_type=FrameType.FULL,
                description="Double parallel lines",
                colorizable=True,
                default_thickness=4.0,
            )
        )

        # Geometric frames (programmatic)
        self._register_frame(
            FrameDefinition(
                name="geometric_corners",
                display_name="Geometric Corners",
                category=FrameCategory.GEOMETRIC,
                frame_type=FrameType.CORNERS,
                description="Angular geometric corner decorations",
                colorizable=True,
                default_thickness=8.0,
            )
        )

        # SVG-based vintage frames
        # Each SVG is designed for a specific corner position:
        # corner_decoration.svg -> top left (tl)
        # corner_ornament.svg -> bottom left (bl)
        # floral_corner.svg -> bottom left (bl)
        # floral_flourish.svg -> bottom right (br)
        # ornate_corner.svg -> top left (tl)
        # simple_corner.svg -> top left (tl)
        corners_dir = self._frames_dir / "corners"

        # Floral Corner (designed for bottom-left)
        if (corners_dir / "floral_corner.svg").exists():
            self._register_frame(
                FrameDefinition(
                    name="floral_corner",
                    display_name="Floral Corner",
                    category=FrameCategory.VINTAGE,
                    frame_type=FrameType.CORNERS,
                    description="Decorative floral corner ornament",
                    asset_path="corners/floral_corner.svg",
                    asset_corner="bl",
                    colorizable=True,
                    default_thickness=12.0,
                )
            )

        # Floral Flourish (designed for bottom-right)
        if (corners_dir / "floral_flourish.svg").exists():
            self._register_frame(
                FrameDefinition(
                    name="floral_flourish",
                    display_name="Floral Flourish",
                    category=FrameCategory.VINTAGE,
                    frame_type=FrameType.CORNERS,
                    description="Elegant floral flourish design",
                    asset_path="corners/floral_flourish.svg",
                    asset_corner="br",
                    colorizable=True,
                    default_thickness=10.0,
                )
            )

        # Ornate Corner (designed for top-left)
        if (corners_dir / "ornate_corner.svg").exists():
            self._register_frame(
                FrameDefinition(
                    name="ornate_corner",
                    display_name="Ornate Corner",
                    category=FrameCategory.VINTAGE,
                    frame_type=FrameType.CORNERS,
                    description="Classic ornate line art corner",
                    asset_path="corners/ornate_corner.svg",
                    asset_corner="tl",
                    colorizable=True,
                    default_thickness=10.0,
                )
            )

        # Simple Corner (designed for top-left)
        if (corners_dir / "simple_corner.svg").exists():
            self._register_frame(
                FrameDefinition(
                    name="simple_corner",
                    display_name="Simple Corner",
                    category=FrameCategory.VINTAGE,
                    frame_type=FrameType.CORNERS,
                    description="Simple decorative corner ornament",
                    asset_path="corners/simple_corner.svg",
                    asset_corner="tl",
                    colorizable=True,
                    default_thickness=8.0,
                )
            )

        # Corner Decoration (designed for top-left)
        if (corners_dir / "corner_decoration.svg").exists():
            self._register_frame(
                FrameDefinition(
                    name="corner_decoration",
                    display_name="Corner Decoration",
                    category=FrameCategory.VINTAGE,
                    frame_type=FrameType.CORNERS,
                    description="Decorative corner piece",
                    asset_path="corners/corner_decoration.svg",
                    asset_corner="tl",
                    colorizable=True,
                    default_thickness=10.0,
                )
            )

        # Corner Ornament (designed for bottom-left)
        if (corners_dir / "corner_ornament.svg").exists():
            self._register_frame(
                FrameDefinition(
                    name="corner_ornament",
                    display_name="Corner Ornament",
                    category=FrameCategory.VINTAGE,
                    frame_type=FrameType.CORNERS,
                    description="Vintage corner ornament design",
                    asset_path="corners/corner_ornament.svg",
                    asset_corner="bl",
                    colorizable=True,
                    default_thickness=10.0,
                )
            )

    def _register_frame(self, frame: FrameDefinition):
        """Register a frame definition"""
        self.frames[frame.name] = frame

    def get_frame(self, name: str) -> Optional[FrameDefinition]:
        """Get a frame by name"""
        return self.frames.get(name)

    def get_frames_by_category(self, category: FrameCategory) -> List[FrameDefinition]:
        """Get all frames in a category"""
        return [f for f in self.frames.values() if f.category == category]

    def get_all_frames(self) -> List[FrameDefinition]:
        """Get all available frames"""
        return list(self.frames.values())

    def get_frame_names(self) -> List[str]:
        """Get list of all frame names"""
        return list(self.frames.keys())

    def _load_svg_as_image(
        self,
        svg_path: Path,
        target_size: int,
        color: Optional[Tuple[int, int, int]] = None,
    ) -> Optional[Image.Image]:
        """
        Load an SVG file and render it to a PIL Image.

        Args:
            svg_path: Path to the SVG file
            target_size: Target size in pixels for the corner
            color: Optional color override as RGB tuple (0-255)

        Returns:
            PIL Image with alpha channel, or None if loading fails
        """
        try:
            import cairosvg
        except ImportError:
            print("Warning: cairosvg not installed, SVG frames will use fallback rendering")
            return None

        # Validate svg_path type
        if not isinstance(svg_path, (str, Path)):
            print(f"Warning: Invalid svg_path type: {type(svg_path)}, expected Path or str")
            return None

        # Ensure svg_path is a Path object
        if isinstance(svg_path, str):
            svg_path = Path(svg_path)

        if not svg_path.exists():
            return None

        try:
            # Read SVG content
            svg_content = svg_path.read_text()

            # Apply color override if specified
            if color is not None:
                svg_content = self._recolor_svg(svg_content, color)

            # Render SVG to PNG bytes
            png_data = cairosvg.svg2png(
                bytestring=svg_content.encode("utf-8"),
                output_width=target_size,
                output_height=target_size,
            )

            # Validate png_data type
            if not isinstance(png_data, bytes):
                print(f"Warning: cairosvg returned {type(png_data)} instead of bytes")
                return None

            # Load as PIL Image from bytes buffer
            buffer = io.BytesIO(png_data)
            img: Image.Image = Image.open(buffer)
            if img.mode != "RGBA":
                img = img.convert("RGBA")

            # Force load the image data to avoid issues with BytesIO going out of scope
            img.load()

            return img

        except Exception as e:
            import traceback

            print(f"Error loading SVG {svg_path}: {e}")
            traceback.print_exc()
            return None

    def _recolor_svg(self, svg_content: str, color: Tuple[int, int, int]) -> str:
        """
        Recolor an SVG by replacing fill and stroke colors.

        Args:
            svg_content: SVG file content as string
            color: New color as RGB tuple (0-255)

        Returns:
            Modified SVG content with new colors
        """
        r, g, b = color
        hex_color = f"#{r:02x}{g:02x}{b:02x}"
        rgb_color = f"rgb({r},{g},{b})"

        # Replace common color patterns
        # Replace fill colors (hex, rgb, named colors)
        svg_content = re.sub(
            r'fill\s*[:=]\s*["\']?(?:#[0-9a-fA-F]{3,6}|rgb\([^)]+\)|black|white|none)["\']?',
            f'fill="{hex_color}"',
            svg_content,
            flags=re.IGNORECASE,
        )

        # Replace stroke colors
        svg_content = re.sub(
            r'stroke\s*[:=]\s*["\']?(?:#[0-9a-fA-F]{3,6}|rgb\([^)]+\)|black|white)["\']?',
            f'stroke="{hex_color}"',
            svg_content,
            flags=re.IGNORECASE,
        )

        # Replace style-based fill/stroke
        svg_content = re.sub(
            r"(fill\s*:\s*)(?:#[0-9a-fA-F]{3,6}|rgb\([^)]+\)|black|white)",
            f"\\1{hex_color}",
            svg_content,
            flags=re.IGNORECASE,
        )
        svg_content = re.sub(
            r"(stroke\s*:\s*)(?:#[0-9a-fA-F]{3,6}|rgb\([^)]+\)|black|white)",
            f"\\1{hex_color}",
            svg_content,
            flags=re.IGNORECASE,
        )

        return svg_content

    def _get_corner_image(
        self,
        frame: FrameDefinition,
        corner_size: int,
        color: Tuple[int, int, int],
    ) -> Optional[Image.Image]:
        """
        Get a corner image, using cache if available.

        Args:
            frame: Frame definition
            corner_size: Size in pixels
            color: Color as RGB tuple

        Returns:
            PIL Image or None
        """
        cache_key = (color, corner_size)

        if cache_key in frame._image_cache:
            return frame._image_cache[cache_key]

        if frame.asset_path:
            try:
                svg_path = self._frames_dir / frame.asset_path
                img = self._load_svg_as_image(svg_path, corner_size, color)
                if img:
                    frame._image_cache[cache_key] = img
                    return img
            except Exception as e:
                import traceback

                print(f"Error getting corner image for {frame.name}: {e}")
                traceback.print_exc()
                return None

        return None

    def render_frame_opengl(
        self,
        frame_name: str,
        x: float,
        y: float,
        width: float,
        height: float,
        color: Tuple[int, int, int] = (0, 0, 0),
        thickness: Optional[float] = None,
        corners: Optional[Tuple[bool, bool, bool, bool]] = None,
    ):
        """
        Render a decorative frame using OpenGL.

        Args:
            frame_name: Name of the frame to render
            x, y: Position of the image
            width, height: Size of the image
            color: Frame color as RGB (0-255)
            thickness: Frame thickness (None = use default)
            corners: Which corners to render (TL, TR, BR, BL). None = all corners
        """
        frame = self.get_frame(frame_name)
        if not frame:
            return

        # Default to all corners if not specified
        if corners is None:
            corners = (True, True, True, True)

        from pyPhotoAlbum.gl_imports import (
            glColor3f,
            glColor4f,
            glBegin,
            glEnd,
            glVertex2f,
            GL_LINE_LOOP,
            glLineWidth,
            glEnable,
            glDisable,
            GL_BLEND,
            glBlendFunc,
            GL_SRC_ALPHA,
            GL_ONE_MINUS_SRC_ALPHA,
            GL_TEXTURE_2D,
            glBindTexture,
            glTexCoord2f,
            GL_QUADS,
        )

        # Calculate thickness
        shorter_side = min(width, height)
        frame_thickness = thickness if thickness else (shorter_side * frame.default_thickness / 100)

        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        # Try to render with SVG asset if available
        if frame.asset_path and frame.frame_type == FrameType.CORNERS:
            corner_size = int(frame_thickness * 2)
            if self._render_svg_corners_gl(frame, x, y, width, height, corner_size, color, corners):
                glDisable(GL_BLEND)
                return

        # Fall back to programmatic rendering
        r, g, b = color[0] / 255.0, color[1] / 255.0, color[2] / 255.0
        glColor3f(r, g, b)

        if frame.frame_type == FrameType.CORNERS:
            self._render_corner_frame_gl(x, y, width, height, frame_thickness, frame_name, corners)
        elif frame.frame_type == FrameType.FULL:
            self._render_full_frame_gl(x, y, width, height, frame_thickness)

        glDisable(GL_BLEND)

    def _render_svg_corners_gl(
        self,
        frame: FrameDefinition,
        x: float,
        y: float,
        w: float,
        h: float,
        corner_size: int,
        color: Tuple[int, int, int],
        corners: Tuple[bool, bool, bool, bool],
    ) -> bool:
        """
        Render SVG-based corners using OpenGL textures.

        Returns True if rendering was successful, False to fall back to programmatic.
        """
        from pyPhotoAlbum.gl_imports import (
            glEnable,
            glDisable,
            glBindTexture,
            glTexCoord2f,
            glVertex2f,
            glBegin,
            glEnd,
            glColor4f,
            GL_TEXTURE_2D,
            GL_QUADS,
            glGenTextures,
            glTexParameteri,
            glTexImage2D,
            GL_TEXTURE_MIN_FILTER,
            GL_TEXTURE_MAG_FILTER,
            GL_LINEAR,
            GL_RGBA,
            GL_UNSIGNED_BYTE,
        )

        # Get or create corner image
        corner_img = self._get_corner_image(frame, corner_size, color)
        if corner_img is None:
            return False

        # Create texture if not cached
        cache_key = (color, corner_size, "texture")
        if cache_key not in frame._texture_cache:
            img_data = corner_img.tobytes()
            texture_id = glGenTextures(1)
            glBindTexture(GL_TEXTURE_2D, texture_id)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
            glTexImage2D(
                GL_TEXTURE_2D,
                0,
                GL_RGBA,
                corner_img.width,
                corner_img.height,
                0,
                GL_RGBA,
                GL_UNSIGNED_BYTE,
                img_data,
            )
            frame._texture_cache[cache_key] = texture_id

        texture_id = frame._texture_cache[cache_key]

        # Render corners
        glEnable(GL_TEXTURE_2D)
        glBindTexture(GL_TEXTURE_2D, texture_id)
        glColor4f(1.0, 1.0, 1.0, 1.0)  # White to show texture colors

        tl, tr, br, bl = corners
        cs = float(corner_size)

        # Helper to draw a textured quad with optional flipping
        # flip_h: flip horizontally, flip_v: flip vertically
        def draw_corner_quad(cx, cy, flip_h=False, flip_v=False):
            # Calculate texture coordinates based on flipping
            u0, u1 = (1, 0) if flip_h else (0, 1)
            v0, v1 = (1, 0) if flip_v else (0, 1)

            glBegin(GL_QUADS)
            glTexCoord2f(u0, v0)
            glVertex2f(cx, cy)
            glTexCoord2f(u1, v0)
            glVertex2f(cx + cs, cy)
            glTexCoord2f(u1, v1)
            glVertex2f(cx + cs, cy + cs)
            glTexCoord2f(u0, v1)
            glVertex2f(cx, cy + cs)
            glEnd()

        # Calculate flips based on the asset's designed corner vs target corner
        # Each SVG is designed for a specific corner (asset_corner field)
        # To render it at a different corner, we flip horizontally and/or vertically
        #
        # Corner positions:
        #   tl (top-left)     tr (top-right)
        #   bl (bottom-left)  br (bottom-right)
        #
        # To go from asset corner to target corner:
        #   - flip_h if horizontal position differs (l->r or r->l)
        #   - flip_v if vertical position differs (t->b or b->t)

        asset_corner = frame.asset_corner  # e.g., "tl", "bl", "br", "tr"
        asset_h = asset_corner[1]  # 'l' or 'r'
        asset_v = asset_corner[0]  # 't' or 'b'

        def get_flips(target_corner: str) -> Tuple[bool, bool]:
            """Calculate flip_h, flip_v to transform from asset_corner to target_corner"""
            target_h = target_corner[1]  # 'l' or 'r'
            target_v = target_corner[0]  # 't' or 'b'
            flip_h = asset_h != target_h
            flip_v = asset_v != target_v
            return flip_h, flip_v

        # Top-left corner
        if tl:
            flip_h, flip_v = get_flips("tl")
            draw_corner_quad(x, y, flip_h=flip_h, flip_v=flip_v)

        # Top-right corner
        if tr:
            flip_h, flip_v = get_flips("tr")
            draw_corner_quad(x + w - cs, y, flip_h=flip_h, flip_v=flip_v)

        # Bottom-right corner
        if br:
            flip_h, flip_v = get_flips("br")
            draw_corner_quad(x + w - cs, y + h - cs, flip_h=flip_h, flip_v=flip_v)

        # Bottom-left corner
        if bl:
            flip_h, flip_v = get_flips("bl")
            draw_corner_quad(x, y + h - cs, flip_h=flip_h, flip_v=flip_v)

        glDisable(GL_TEXTURE_2D)
        return True

    def _render_corner_frame_gl(
        self,
        x: float,
        y: float,
        w: float,
        h: float,
        thickness: float,
        frame_name: str,
        corners: Tuple[bool, bool, bool, bool] = (True, True, True, True),
    ):
        """Render corner-style frame decorations (programmatic fallback)."""
        from pyPhotoAlbum.gl_imports import glBegin, glEnd, glVertex2f, glLineWidth, GL_LINE_STRIP

        corner_size = thickness * 2

        glLineWidth(2.0)

        tl, tr, br, bl = corners

        # Top-left corner
        if tl:
            glBegin(GL_LINE_STRIP)
            glVertex2f(x, y + corner_size)
            glVertex2f(x, y)
            glVertex2f(x + corner_size, y)
            glEnd()

        # Top-right corner
        if tr:
            glBegin(GL_LINE_STRIP)
            glVertex2f(x + w - corner_size, y)
            glVertex2f(x + w, y)
            glVertex2f(x + w, y + corner_size)
            glEnd()

        # Bottom-right corner
        if br:
            glBegin(GL_LINE_STRIP)
            glVertex2f(x + w, y + h - corner_size)
            glVertex2f(x + w, y + h)
            glVertex2f(x + w - corner_size, y + h)
            glEnd()

        # Bottom-left corner
        if bl:
            glBegin(GL_LINE_STRIP)
            glVertex2f(x + corner_size, y + h)
            glVertex2f(x, y + h)
            glVertex2f(x, y + h - corner_size)
            glEnd()

        # Add decorative swirls for vintage frames
        if "leafy" in frame_name or "ornate" in frame_name or "flourish" in frame_name:
            self._render_decorative_swirls_gl(x, y, w, h, corner_size, corners)

        glLineWidth(1.0)

    def _render_decorative_swirls_gl(
        self,
        x: float,
        y: float,
        w: float,
        h: float,
        size: float,
        corners: Tuple[bool, bool, bool, bool] = (True, True, True, True),
    ):
        """Render decorative swirl elements at corners (programmatic fallback)."""
        from pyPhotoAlbum.gl_imports import glBegin, glEnd, glVertex2f, GL_LINE_STRIP
        import math

        steps = 8
        radius = size * 0.4

        tl, tr, br, bl = corners

        corner_data = [
            (tl, x + size * 0.5, y + size * 0.5, math.pi),
            (tr, x + w - size * 0.5, y + size * 0.5, math.pi * 1.5),
            (br, x + w - size * 0.5, y + h - size * 0.5, 0),
            (bl, x + size * 0.5, y + h - size * 0.5, math.pi * 0.5),
        ]

        for enabled, cx, cy, start_angle in corner_data:
            if not enabled:
                continue
            glBegin(GL_LINE_STRIP)
            for i in range(steps + 1):
                angle = start_angle + (math.pi * 0.5 * i / steps)
                px = cx + radius * math.cos(angle)
                py = cy + radius * math.sin(angle)
                glVertex2f(px, py)
            glEnd()

    def _render_full_frame_gl(self, x: float, y: float, w: float, h: float, thickness: float):
        """Render full-border frame (programmatic)"""
        from pyPhotoAlbum.gl_imports import glBegin, glEnd, glVertex2f, GL_LINE_LOOP, glLineWidth

        glLineWidth(max(1.0, thickness * 0.5))
        glBegin(GL_LINE_LOOP)
        glVertex2f(x - thickness * 0.5, y - thickness * 0.5)
        glVertex2f(x + w + thickness * 0.5, y - thickness * 0.5)
        glVertex2f(x + w + thickness * 0.5, y + h + thickness * 0.5)
        glVertex2f(x - thickness * 0.5, y + h + thickness * 0.5)
        glEnd()

        glBegin(GL_LINE_LOOP)
        glVertex2f(x + thickness * 0.3, y + thickness * 0.3)
        glVertex2f(x + w - thickness * 0.3, y + thickness * 0.3)
        glVertex2f(x + w - thickness * 0.3, y + h - thickness * 0.3)
        glVertex2f(x + thickness * 0.3, y + h - thickness * 0.3)
        glEnd()

        glLineWidth(1.0)

    def render_frame_pdf(
        self,
        canvas,
        frame_name: str,
        x_pt: float,
        y_pt: float,
        width_pt: float,
        height_pt: float,
        color: Tuple[int, int, int] = (0, 0, 0),
        thickness_pt: Optional[float] = None,
        corners: Optional[Tuple[bool, bool, bool, bool]] = None,
    ):
        """
        Render a decorative frame on a PDF canvas.

        Args:
            canvas: ReportLab canvas
            frame_name: Name of the frame to render
            x_pt, y_pt: Position in points
            width_pt, height_pt: Size in points
            color: Frame color as RGB (0-255)
            thickness_pt: Frame thickness in points (None = use default)
            corners: Which corners to render (TL, TR, BR, BL). None = all corners
        """
        frame = self.get_frame(frame_name)
        if not frame:
            return

        if corners is None:
            corners = (True, True, True, True)

        shorter_side = min(width_pt, height_pt)
        frame_thickness = thickness_pt if thickness_pt else (shorter_side * frame.default_thickness / 100)

        r, g, b = color[0] / 255.0, color[1] / 255.0, color[2] / 255.0

        canvas.saveState()
        canvas.setStrokeColorRGB(r, g, b)
        canvas.setLineWidth(max(0.5, frame_thickness * 0.3))

        # Try SVG rendering for PDF
        if frame.asset_path and frame.frame_type == FrameType.CORNERS:
            corner_size_pt = frame_thickness * 2
            if self._render_svg_corners_pdf(
                canvas, frame, x_pt, y_pt, width_pt, height_pt, corner_size_pt, color, corners
            ):
                canvas.restoreState()
                return

        # Fall back to programmatic
        if frame.frame_type == FrameType.CORNERS:
            self._render_corner_frame_pdf(canvas, x_pt, y_pt, width_pt, height_pt, frame_thickness, frame_name, corners)
        elif frame.frame_type == FrameType.FULL:
            self._render_full_frame_pdf(canvas, x_pt, y_pt, width_pt, height_pt, frame_thickness)

        canvas.restoreState()

    def _render_svg_corners_pdf(
        self,
        canvas,
        frame: FrameDefinition,
        x: float,
        y: float,
        w: float,
        h: float,
        corner_size_pt: float,
        color: Tuple[int, int, int],
        corners: Tuple[bool, bool, bool, bool],
    ) -> bool:
        """Render SVG corners on PDF canvas. Returns True if successful."""
        from reportlab.lib.utils import ImageReader

        # Get corner image at high resolution for PDF
        corner_size_px = int(corner_size_pt * 4)  # 4x for PDF quality
        if corner_size_px < 1:
            corner_size_px = 1
        corner_img = self._get_corner_image(frame, corner_size_px, color)
        if corner_img is None:
            return False

        tl, tr, br, bl = corners
        cs = corner_size_pt

        # For PDF, we use PIL to flip the image rather than canvas transformations
        # This is more reliable across different PDF renderers
        def get_flipped_image(target_corner: str) -> Image.Image:
            """Get image flipped appropriately for the target corner"""
            asset_corner = frame.asset_corner
            asset_h = asset_corner[1]  # 'l' or 'r'
            asset_v = asset_corner[0]  # 't' or 'b'
            target_h = target_corner[1]
            target_v = target_corner[0]

            img = corner_img.copy()

            # Flip horizontally if h position differs
            if asset_h != target_h:
                img = img.transpose(Image.Transpose.FLIP_LEFT_RIGHT)

            # Flip vertically if v position differs
            if asset_v != target_v:
                img = img.transpose(Image.Transpose.FLIP_TOP_BOTTOM)

            return img

        # Note: PDF Y-axis is bottom-up, so corners are positioned differently
        # Top-left in screen coordinates = high Y in PDF
        if tl:
            img = get_flipped_image("tl")
            img_reader = ImageReader(img)
            canvas.drawImage(img_reader, x, y + h - cs, cs, cs, mask="auto")

        # Top-right
        if tr:
            img = get_flipped_image("tr")
            img_reader = ImageReader(img)
            canvas.drawImage(img_reader, x + w - cs, y + h - cs, cs, cs, mask="auto")

        # Bottom-right
        if br:
            img = get_flipped_image("br")
            img_reader = ImageReader(img)
            canvas.drawImage(img_reader, x + w - cs, y, cs, cs, mask="auto")

        # Bottom-left
        if bl:
            img = get_flipped_image("bl")
            img_reader = ImageReader(img)
            canvas.drawImage(img_reader, x, y, cs, cs, mask="auto")

        return True

    def _render_corner_frame_pdf(
        self,
        canvas,
        x: float,
        y: float,
        w: float,
        h: float,
        thickness: float,
        frame_name: str,
        corners: Tuple[bool, bool, bool, bool] = (True, True, True, True),
    ):
        """Render corner-style frame on PDF (programmatic fallback)."""
        corner_size = thickness * 2
        tl, tr, br, bl = corners

        path = canvas.beginPath()

        if tl:
            path.moveTo(x, y + h - corner_size)
            path.lineTo(x, y + h)
            path.lineTo(x + corner_size, y + h)

        if tr:
            path.moveTo(x + w - corner_size, y + h)
            path.lineTo(x + w, y + h)
            path.lineTo(x + w, y + h - corner_size)

        if br:
            path.moveTo(x + w, y + corner_size)
            path.lineTo(x + w, y)
            path.lineTo(x + w - corner_size, y)

        if bl:
            path.moveTo(x + corner_size, y)
            path.lineTo(x, y)
            path.lineTo(x, y + corner_size)

        canvas.drawPath(path, stroke=1, fill=0)

    def _render_full_frame_pdf(self, canvas, x: float, y: float, w: float, h: float, thickness: float):
        """Render full-border frame on PDF"""
        canvas.rect(
            x - thickness * 0.5,
            y - thickness * 0.5,
            w + thickness,
            h + thickness,
            stroke=1,
            fill=0,
        )

        canvas.rect(
            x + thickness * 0.3,
            y + thickness * 0.3,
            w - thickness * 0.6,
            h - thickness * 0.6,
            stroke=1,
            fill=0,
        )


# Global frame manager instance
_frame_manager: Optional[FrameManager] = None


def get_frame_manager() -> FrameManager:
    """Get the global frame manager instance"""
    global _frame_manager
    if _frame_manager is None:
        _frame_manager = FrameManager()
    return _frame_manager
