analyze this code to see how it can be refactored better for dont repeat yourself principle and for better maintainability:
```
import uuid
import random
import time
import json
from typing import List, Optional, Dict, Any
from enum import Enum
from abc import ABC, abstractmethod
import re

# Enums for predefined constants
class StrokeStyle(Enum):
    SOLID = "solid"
    DASHED = "dashed"
    DOTTED = "dotted"

class FillStyle(Enum):
    SOLID = "solid"
    HACHURE = "hachure"
    CROSS_HATCH = "cross-hatch"

class RoundnessType(Enum):
    NONE = 0
    MILD = 1
    MODERATE = 2
    ROUND = 3

class ArrowType(Enum):
    SHARP = "sharp"
    CURVED = "curved"
    ELBOW = "elbow"

class ArrowHead(Enum):
    NONE = "none"
    ARROW = "arrow"
    BAR = "bar"
    TRIANGLE = "triangle"

class FontStyle(Enum):
    COMIC_SANS = "Comic Sans"
    EXCALIFONT = "Excalifont"
    LILITA_ONE = "Lilita One"
    NUNITO = "Nunito"
    NORMAL = "Normal"  # Added 'Normal' for default

class FontFamily(Enum):
    HANDDRAWN = "handdrawn"
    NORMAL = "normal"
    CODE = "code"

class TextAlign(Enum):
    LEFT = "left"
    MIDDLE = "middle"
    RIGHT = "right"

class VerticalAlign(Enum):
    TOP = "top"
    MIDDLE = "middle"
    BOTTOM = "bottom"

# Base class for all elements
class ExcalidrawElement(ABC):
    def __init__(
        self,
        element_type: str,
        x: float,
        y: float,
        width: float,
        height: float,
        stroke_color: str = "#000000",
        background_color: str = "transparent",
        fill_style: FillStyle = FillStyle.SOLID,
        stroke_width: int = 1,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        roundness_type: Optional[RoundnessType] = RoundnessType.NONE,
        angle: float = 0.0,  # Rotation in radians
    ):
        self.id = str(uuid.uuid4()).replace('-', '')
        self.type = element_type
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.angle = angle  # Rotation in radians
        self.strokeColor = stroke_color
        self.backgroundColor = background_color
        self.fillStyle = fill_style.value if fill_style else "solid"
        self.strokeWidth = stroke_width
        self.strokeStyle = stroke_style.value if stroke_style else "solid"
        self.roughness = roughness
        self.opacity = opacity
        self.groupIds = []
        self.frameId = None
        self.index = ""  # To be set by the container
        self.roundness = {"type": roundness_type.value} if roundness_type and roundness_type != RoundnessType.NONE else None
        self.seed = random.randint(0, 2**32)
        self.version = 1
        self.versionNonce = random.randint(0, 2**32)
        self.isDeleted = False
        self.boundElements = None
        self.updated = int(time.time() * 1000)
        self.link = None
        self.locked = False

    @abstractmethod
    def to_dict(self, index: str) -> Dict[str, Any]:
        pass

# Rectangle element
class Rectangle(ExcalidrawElement):
    def __init__(self, **kwargs):
        super().__init__(element_type="rectangle", **kwargs)

    def to_dict(self, index: str) -> Dict[str, Any]:
        element = {
            "id": self.id,
            "type": self.type,
            "x": self.x,
            "y": self.y,
            "width": self.width,
            "height": self.height,
            "angle": self.angle,
            "strokeColor": self.strokeColor,
            "backgroundColor": self.backgroundColor,
            "fillStyle": self.fillStyle,
            "strokeWidth": self.strokeWidth,
            "strokeStyle": self.strokeStyle,
            "roughness": self.roughness,
            "opacity": self.opacity,
            "groupIds": self.groupIds,
            "frameId": self.frameId,
            "index": index,
            "seed": self.seed,
            "version": self.version,
            "versionNonce": self.versionNonce,
            "isDeleted": self.isDeleted,
            "boundElements": self.boundElements,
            "updated": self.updated,
            "link": self.link,
            "locked": self.locked,
        }
        if self.roundness is not None:
            element["roundness"] = self.roundness
        return element

# Diamond element
class Diamond(ExcalidrawElement):
    def __init__(self, **kwargs):
        super().__init__(element_type="diamond", **kwargs)

    def to_dict(self, index: str) -> Dict[str, Any]:
        element = {
            "id": self.id,
            "type": self.type,
            "x": self.x,
            "y": self.y,
            "width": self.width,
            "height": self.height,
            "angle": self.angle,
            "strokeColor": self.strokeColor,
            "backgroundColor": self.backgroundColor,
            "fillStyle": self.fillStyle,
            "strokeWidth": self.strokeWidth,
            "strokeStyle": self.strokeStyle,
            "roughness": self.roughness,
            "opacity": self.opacity,
            "groupIds": self.groupIds,
            "frameId": self.frameId,
            "index": index,
            "seed": self.seed,
            "version": self.version,
            "versionNonce": self.versionNonce,
            "isDeleted": self.isDeleted,
            "boundElements": self.boundElements,
            "updated": self.updated,
            "link": self.link,
            "locked": self.locked,
        }
        if self.roundness is not None:
            element["roundness"] = self.roundness
        return element

# Ellipse element
class Ellipse(ExcalidrawElement):
    def __init__(self, **kwargs):
        super().__init__(element_type="ellipse", **kwargs)
        # Ellipse does not use roundness

    def to_dict(self, index: str) -> Dict[str, Any]:
        element = {
            "id": self.id,
            "type": self.type,
            "x": self.x,
            "y": self.y,
            "width": self.width,
            "height": self.height,
            "angle": self.angle,
            "strokeColor": self.strokeColor,
            "backgroundColor": self.backgroundColor,
            "fillStyle": self.fillStyle,
            "strokeWidth": self.strokeWidth,
            "strokeStyle": self.strokeStyle,
            "roughness": self.roughness,
            "opacity": self.opacity,
            "groupIds": self.groupIds,
            "frameId": self.frameId,
            "index": index,
            "seed": self.seed,
            "version": self.version,
            "versionNonce": self.versionNonce,
            "isDeleted": self.isDeleted,
            "boundElements": self.boundElements,
            "updated": self.updated,
            "link": self.link,
            "locked": self.locked,
        }
        # Ellipse does not have roundness
        return element

# Arrow element
class Arrow(ExcalidrawElement):
    def __init__(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        stroke_color: str = "#000000",
        stroke_width: int = 1,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        arrow_type: ArrowType = ArrowType.SHARP,
        arrow_head_left: ArrowHead = ArrowHead.NONE,
        arrow_head_right: ArrowHead = ArrowHead.ARROW,
        points: Optional[List[List[float]]] = None,
        angle: float = 0.0
    ):
        super().__init__(
            element_type="arrow",
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            background_color="transparent",  # Arrows typically don't have background colors
            fill_style=FillStyle.SOLID,  # Not used for arrows but required by the base class
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=None,  # Arrows don't have roundness
            angle=angle
        )
        self.points = points if points else [
            [0, 0],
            [width, height]
        ]
        self.lastCommittedPoint = None
        self.startBinding = None
        self.endBinding = None
        self.startArrowhead = arrow_head_left.value
        self.endArrowhead = arrow_head_right.value
        self.elbowed = arrow_type == ArrowType.ELBOW

    def to_dict(self, index: str) -> Dict[str, Any]:
        element = super().to_dict(index)
        element.update({
            "points": self.points,
            "lastCommittedPoint": self.lastCommittedPoint,
            "startBinding": self.startBinding,
            "endBinding": self.endBinding,
            "startArrowhead": self.startArrowhead,
            "endArrowhead": self.endArrowhead,
            "elbowed": self.elbowed,
        })
        return element

# Line element
class Line(ExcalidrawElement):
    def __init__(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        stroke_color: str = "#000000",
        background_color: str = "#b2f2bb",
        fill_style: FillStyle = FillStyle.SOLID,
        stroke_width: int = 2,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        roundness_type: RoundnessType = RoundnessType.MODERATE,
        points: Optional[List[List[float]]] = None,
        angle: float = 0.0
    ):
        super().__init__(
            element_type="line",
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            background_color=background_color,
            fill_style=fill_style,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=roundness_type,
            angle=angle
        )
        self.points = points if points else [
            [0, 0],
            [width, height]
        ]
        self.lastCommittedPoint = None
        self.startBinding = None
        self.endBinding = None
        self.startArrowhead = ArrowHead.NONE.value
        self.endArrowhead = ArrowHead.NONE.value
        self.elbowed = False  # Lines typically don't have elbowed paths unless specified

    def to_dict(self, index: str) -> Dict[str, Any]:
        element = super().to_dict(index)
        element.update({
            "points": self.points,
            "lastCommittedPoint": self.lastCommittedPoint,
            "startBinding": self.startBinding,
            "endBinding": self.endBinding,
            "startArrowhead": self.startArrowhead,
            "endArrowhead": self.endArrowhead,
            "elbowed": self.elbowed,
        })
        return element

# Text element
class Text(ExcalidrawElement):
    def __init__(
        self,
        x: float,
        y: float,
        text: str,
        font_size: int = 16,
        font_style: FontStyle = FontStyle.NORMAL,
        font_family: FontFamily = FontFamily.NORMAL,
        text_align: TextAlign = TextAlign.LEFT,
        vertical_align: VerticalAlign = VerticalAlign.TOP,
        line_height: float = 1.15,
        stroke_color: str = "#000000",
        background_color: str = "transparent",
        fill_style: FillStyle = FillStyle.SOLID,
        stroke_width: int = 2,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 80,
        roundness_type: Optional[RoundnessType] = RoundnessType.NONE,
        angle: float = 0.0
    ):
        super().__init__(
            element_type="text",
            x=x,
            y=y,
            width=0,  # Width is auto-calculated if autoResize is True
            height=0,  # Height is auto-calculated if autoResize is True
            stroke_color=stroke_color,
            background_color=background_color,
            fill_style=fill_style,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=roundness_type,
            angle=angle
        )
        self.text = text
        self.fontSize = font_size
        self.fontStyle = font_style.value
        self.fontFamily = font_family.value
        self.textAlign = text_align.value
        self.verticalAlign = vertical_align.value
        self.lineHeight = line_height
        self.autoResize = True
        self.originalText = text
        self.containerId = None

    def to_dict(self, index: str) -> Dict[str, Any]:
        element = {
            "id": self.id,
            "type": self.type,
            "x": self.x,
            "y": self.y,
            "width": self.width,
            "height": self.height,
            "angle": self.angle,
            "strokeColor": self.strokeColor,
            "backgroundColor": self.backgroundColor,
            "fillStyle": self.fillStyle,
            "strokeWidth": self.strokeWidth,
            "strokeStyle": self.strokeStyle,
            "roughness": self.roughness,
            "opacity": self.opacity,
            "groupIds": self.groupIds,
            "frameId": self.frameId,
            "index": index,
            "seed": self.seed,
            "version": self.version,
            "versionNonce": self.versionNonce,
            "isDeleted": self.isDeleted,
            "boundElements": self.boundElements,
            "updated": self.updated,
            "link": self.link,
            "locked": self.locked,
            "text": self.text,
            "fontSize": self.fontSize,
            "fontFamily": self.fontFamily,
            "textAlign": self.textAlign,
            "verticalAlign": self.verticalAlign,
            "originalText": self.originalText,
            "autoResize": self.autoResize,
            "lineHeight": self.lineHeight
        }
        if self.roundness is not None:
            element["roundness"] = self.roundness
        if self.containerId:
            element["containerId"] = self.containerId
        return element

# Freedraw element
class Freedraw(ExcalidrawElement):
    def __init__(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        points: Optional[List[List[float]]] = None,
        stroke_color: str = "#000000",
        background_color: str = "transparent",
        fill_style: FillStyle = FillStyle.SOLID,
        stroke_width: int = 2,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        roundness_type: Optional[RoundnessType] = RoundnessType.NONE,
        angle: float = 0.0
    ):
        super().__init__(
            element_type="freedraw",
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            background_color=background_color,
            fill_style=fill_style,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=roundness_type,
            angle=angle
        )
        self.points = points if points else [
            [0, 0],
            [width, height]
        ]
        self.lastCommittedPoint = None
        self.startBinding = None
        self.endBinding = None
        self.startArrowhead = ArrowHead.NONE.value
        self.endArrowhead = ArrowHead.NONE.value
        self.elbowed = False  # Freedraw typically doesn't have elbowed paths

    def to_dict(self, index: str) -> Dict[str, Any]:
        element = super().to_dict(index)
        element.update({
            "points": self.points,
            "lastCommittedPoint": self.lastCommittedPoint,
            "startBinding": self.startBinding,
            "endBinding": self.endBinding,
            "startArrowhead": self.startArrowhead,
            "endArrowhead": self.endArrowhead,
            "elbowed": self.elbowed,
        })
        return element

# Image element
class ImageElement(ExcalidrawElement):
    SUPPORTED_MIME_TYPES = {
        "image/jpeg",
        "image/jpg",
        "image/png",
        "image/svg+xml",
        "image/avif",
        "image/webp",
        "image/gif",
        "image/bmp",
        "image/x-icon",
        "image/jfif",
        "image/svg+xml",
        "image/svgz",
        "image/pjpeg",
        "image/pjp",
        "image/vnd.microsoft.icon",
        "image/dib",
    }

    def __init__(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        mime_type: str,
        base64_data: str,
        stroke_color: str = "transparent",
        background_color: str = "#ffec99",
        fill_style: FillStyle = FillStyle.CROSS_HATCH,
        stroke_width: int = 2,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 50,
        angle: float = 0.0,
        scale: Optional[List[float]] = None,
        status: str = "saved"
    ):
        super().__init__(
            element_type="image",
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            background_color=background_color,
            fill_style=fill_style,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=None,  # Images do not have roundness
            angle=angle
        )
        self.status = status
        self.fileId = ""  # To be set when added to document
        self.scale = scale if scale else [1, 1]

        # Validate MIME type
        if mime_type not in self.SUPPORTED_MIME_TYPES:
            raise ValueError(f"Unsupported MIME type: {mime_type}")
        self.mimeType = mime_type

        # Validate base64 data
        if not re.fullmatch(r"data:(image/(?:jpeg|jpg|png|svg\+xml|avif|webp|gif|bmp|x-icon|jfif|svgz|pjpeg|pjp|vnd\.microsoft\.icon|dib));base64,[A-Za-z0-9+/=]+", base64_data):
            raise ValueError("Invalid base64 data for image.")

        self.dataURL = base64_data

    def to_dict(self, index: str) -> Dict[str, Any]:
        element = super().to_dict(index)
        element.update({
            "status": self.status,
            "fileId": self.fileId,
            "scale": self.scale,
        })
        return element

# ExcalidrawDocument class
class ExcalidrawDocument:
    SUPPORTED_IMAGE_MIME_TYPES = {
        "image/jpeg",
        "image/jpg",
        "image/png",
        "image/svg+xml",
        "image/avif",
        "image/webp",
        "image/gif",
        "image/bmp",
        "image/x-icon",
        "image/jfif",
        "image/svgz",
        "image/pjpeg",
        "image/pjp",
        "image/vnd.microsoft.icon",
        "image/dib",
    }

    def __init__(
        self,
        grid_size: int = 20,
        grid_step: int = 5,
        grid_mode_enabled: bool = False,
        view_background_color: str = "#ffffff"
    ):
        self.elements: List[ExcalidrawElement] = []
        self.appState = {
            "gridSize": grid_size,
            "gridStep": grid_step,
            "gridModeEnabled": grid_mode_enabled,
            "viewBackgroundColor": view_background_color
        }
        self.files: Dict[str, Any] = {}
        self.index_counter = 0  # To keep track of ordering

    def add_rectangle(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        stroke_color: str = "#000000",
        background_color: str = "transparent",
        stroke_width: int = 1,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        fill_style: FillStyle = FillStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        roundness_type: Optional[RoundnessType] = RoundnessType.NONE,
        angle: float = 0.0
    ) -> None:
        self._validate_color(stroke_color)
        self._validate_color(background_color)
        self._validate_dimensions(width, height)

        rectangle = Rectangle(
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            background_color=background_color,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            fill_style=fill_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=roundness_type,
            angle=angle
        )
        self.elements.append(rectangle)

    def add_diamond(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        stroke_color: str = "#000000",
        background_color: str = "transparent",
        stroke_width: int = 1,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        fill_style: FillStyle = FillStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        roundness_type: Optional[RoundnessType] = RoundnessType.NONE,
        angle: float = 0.0
    ) -> None:
        self._validate_color(stroke_color)
        self._validate_color(background_color)
        self._validate_dimensions(width, height)

        diamond = Diamond(
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            background_color=background_color,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            fill_style=fill_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=roundness_type,
            angle=angle
        )
        self.elements.append(diamond)

    def add_ellipse(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        stroke_color: str = "#000000",
        background_color: str = "transparent",
        stroke_width: int = 1,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        fill_style: FillStyle = FillStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        angle: float = 0.0
    ) -> None:
        self._validate_color(stroke_color)
        self._validate_color(background_color)
        self._validate_dimensions(width, height)

        ellipse = Ellipse(
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            background_color=background_color,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            fill_style=fill_style,
            roughness=roughness,
            opacity=opacity,
            angle=angle
            # Ellipse does not use roundness
        )
        self.elements.append(ellipse)

    def add_arrow(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        stroke_color: str = "#000000",
        stroke_width: int = 1,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        arrow_type: ArrowType = ArrowType.SHARP,
        arrow_head_left: ArrowHead = ArrowHead.NONE,
        arrow_head_right: ArrowHead = ArrowHead.ARROW,
        points: Optional[List[List[float]]] = None,
        angle: float = 0.0
    ) -> None:
        self._validate_color(stroke_color)
        self._validate_dimensions(width, height)

        arrow = Arrow(
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            arrow_type=arrow_type,
            arrow_head_left=arrow_head_left,
            arrow_head_right=arrow_head_right,
            points=points,
            angle=angle
        )
        self.elements.append(arrow)

    def add_line(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        stroke_color: str = "#000000",
        background_color: str = "#b2f2bb",
        fill_style: FillStyle = FillStyle.SOLID,
        stroke_width: int = 2,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        roundness_type: RoundnessType = RoundnessType.MODERATE,
        points: Optional[List[List[float]]] = None,
        angle: float = 0.0
    ) -> None:
        self._validate_color(stroke_color)
        self._validate_color(background_color)
        self._validate_dimensions(width, height)

        line = Line(
            x=x,
            y=y,
            width=width,
            height=height,
            stroke_color=stroke_color,
            background_color=background_color,
            fill_style=fill_style,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=roundness_type,
            points=points,
            angle=angle
        )
        self.elements.append(line)

    def add_text(
        self,
        x: float,
        y: float,
        text: str,
        font_size: int = 16,
        font_style: FontStyle = FontStyle.NORMAL,
        font_family: FontFamily = FontFamily.NORMAL,
        text_align: TextAlign = TextAlign.LEFT,
        vertical_align: VerticalAlign = VerticalAlign.TOP,
        line_height: float = 1.15,
        stroke_color: str = "#000000",
        background_color: str = "transparent",
        fill_style: FillStyle = FillStyle.SOLID,
        stroke_width: int = 2,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 80,
        roundness_type: Optional[RoundnessType] = RoundnessType.NONE,
        angle: float = 0.0
    ) -> None:
        self._validate_color(stroke_color)
        self._validate_color(background_color)

        text_element = Text(
            x=x,
            y=y,
            text=text,
            font_size=font_size,
            font_style=font_style,
            font_family=font_family,
            text_align=text_align,
            vertical_align=vertical_align,
            line_height=line_height,
            stroke_color=stroke_color,
            background_color=background_color,
            fill_style=fill_style,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=roundness_type,
            angle=angle
        )
        self.elements.append(text_element)

    def add_freedraw(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        points: Optional[List[List[float]]] = None,
        stroke_color: str = "#000000",
        background_color: str = "transparent",
        fill_style: FillStyle = FillStyle.SOLID,
        stroke_width: int = 2,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 100,
        roundness_type: Optional[RoundnessType] = RoundnessType.NONE,
        angle: float = 0.0
    ) -> None:
        self._validate_color(stroke_color)
        self._validate_color(background_color)
        self._validate_dimensions(width, height)

        freedraw_element = Freedraw(
            x=x,
            y=y,
            width=width,
            height=height,
            points=points,
            stroke_color=stroke_color,
            background_color=background_color,
            fill_style=fill_style,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            roundness_type=roundness_type,
            angle=angle
        )
        self.elements.append(freedraw_element)

    def add_image(
        self,
        x: float,
        y: float,
        width: float,
        height: float,
        mime_type: str,
        base64_data: str,
        stroke_color: str = "transparent",
        background_color: str = "#ffec99",
        fill_style: FillStyle = FillStyle.CROSS_HATCH,
        stroke_width: int = 2,
        stroke_style: StrokeStyle = StrokeStyle.SOLID,
        roughness: int = 1,
        opacity: int = 50,
        angle: float = 0.0,
        scale: Optional[List[float]] = None,
        status: str = "saved"
    ) -> None:
        self._validate_image_mime_type(mime_type)
        self._validate_base64_data(mime_type, base64_data)
        self._validate_dimensions(width, height)
        self._validate_color(stroke_color)
        self._validate_color(background_color)

        image = ImageElement(
            x=x,
            y=y,
            width=width,
            height=height,
            mime_type=mime_type,
            base64_data=base64_data,
            stroke_color=stroke_color,
            background_color=background_color,
            fill_style=fill_style,
            stroke_width=stroke_width,
            stroke_style=stroke_style,
            roughness=roughness,
            opacity=opacity,
            angle=angle,
            scale=scale,
            status=status
        )

        # Generate a unique fileId
        file_id = str(uuid.uuid4()).replace('-', '')
        image.fileId = file_id

        # Add file to files dict
        current_time = int(time.time() * 1000)
        self.files[file_id] = {
            "mimeType": mime_type,
            "id": file_id,
            "dataURL": base64_data,
            "created": current_time,
            "lastRetrieved": current_time
        }

        self.elements.append(image)

    def set_app_state(
        self,
        grid_size: Optional[int] = None,
        grid_step: Optional[int] = None,
        grid_mode_enabled: Optional[bool] = None,
        view_background_color: Optional[str] = None
    ) -> None:
        if grid_size is not None:
            self.appState["gridSize"] = grid_size
        if grid_step is not None:
            self.appState["gridStep"] = grid_step
        if grid_mode_enabled is not None:
            self.appState["gridModeEnabled"] = grid_mode_enabled
        if view_background_color is not None:
            self._validate_color(view_background_color)
            self.appState["viewBackgroundColor"] = view_background_color

    def to_json(self) -> str:
        excalidraw_json = {
            "type": "excalidraw",
            "version": 2,
            "source": "https://excalidraw.com",
            "elements": [],
            "appState": self.appState,
            "files": self.files
        }

        for element in self.elements:
            self.index_counter += 1
            # Generate index like aA, aB, ..., aZ, aAA, aAB, etc.
            suffix = ""
            num = self.index_counter
            while num > 0:
                num, rem = divmod(num - 1, 26)
                suffix = chr(65 + rem) + suffix
            prefix = 'a'
            index = f"{prefix}{suffix}"
            excalidraw_json["elements"].append(element.to_dict(index=index))

        return json.dumps(excalidraw_json, indent=2)

    def _validate_color(self, color: str) -> None:
        if not re.fullmatch(r"#(?:[0-9a-fA-F]{3}){1,2}", color):
            raise ValueError(f"Invalid color code: {color}")

    def _validate_dimensions(self, width: float, height: float) -> None:
        if width <= 0 or height <= 0:
            raise ValueError("Width and height must be positive numbers.")

    def _validate_image_mime_type(self, mime_type: str) -> None:
        if mime_type not in self.SUPPORTED_IMAGE_MIME_TYPES:
            raise ValueError(f"Unsupported image MIME type: {mime_type}")

    def _validate_base64_data(self, mime_type: str, base64_data: str) -> None:
        pattern = rf"data:{re.escape(mime_type)};base64,[A-Za-z0-9+/=]+"
        if not re.fullmatch(pattern, base64_data):
            raise ValueError("Invalid base64 data for the provided MIME type.")
```