from pydantic import BaseModel, Field


class NormalizedClickCoordinates(BaseModel):
    """Represents a click position with x, y coordinates, normalized to the image width and height."""

    x: float = Field(ge=0.0, le=1.0)
    y: float = Field(ge=0.0, le=1.0)

    @classmethod
    def from_xy(
        cls, xy: tuple[float, float], image_width: int = 1, image_height: int = 1
    ) -> "NormalizedClickCoordinates":
        """Create a NormalizedClickCoordinates from a tuple of coordinates."""
        return cls(x=xy[0] / image_width, y=xy[1] / image_height)

    def to_xy_string(self) -> str:
        """Convert the click position to a click center position string."""
        return f"{self.x:.3f}, {self.y:.3f}"

    def to_xy(self) -> tuple[float, float]:
        """Convert the click position to a click center position."""
        return (self.x, self.y)


class BoundingBox(BaseModel):
    """Represents a normalized bounding box with x, y, width, height coordinates."""

    x: float = Field(ge=0.0, le=1.0)
    y: float = Field(ge=0.0, le=1.0)
    width: float = Field(ge=0.0, le=1.0)
    height: float = Field(ge=0.0, le=1.0)

    @classmethod
    def from_xywh(
        cls, xywh_bbox: tuple[float, float, float, float], image_width: int = 1, image_height: int = 1
    ) -> "BoundingBox":
        return cls(
            x=xywh_bbox[0] / image_width,
            y=xywh_bbox[1] / image_height,
            width=xywh_bbox[2] / image_width,
            height=xywh_bbox[3] / image_height,
        )

    @classmethod
    def from_xyxy(
        cls, xyxy_bbox: tuple[float, float, float, float], image_width: int = 1, image_height: int = 1
    ) -> "BoundingBox":
        return cls(
            x=xyxy_bbox[0] / image_width,
            y=xyxy_bbox[1] / image_height,
            width=(xyxy_bbox[2] - xyxy_bbox[0]) / image_width,
            height=(xyxy_bbox[3] - xyxy_bbox[1]) / image_height,
        )

    @classmethod
    def from_xywh_string(cls, bbox_str: str, image_width: int, image_height: int) -> "BoundingBox":
        """Create a BoundingBox from a comma-separated string of coordinates."""
        x, y, w, h = map(float, bbox_str.split(","))
        return cls(x=x / image_width, y=y / image_height, width=w / image_width, height=h / image_height)

    def to_click_center_position_string(self) -> str:
        """Convert the bounding box to a click center position string."""
        return f"{self.x + self.width / 2:.3f}, {self.y + self.height / 2:.3f}"

    def to_click_center_position(self) -> tuple[float, float]:
        """Convert the bounding box to a click center position."""
        return (self.x + self.width / 2, self.y + self.height / 2)

    def to_xyxy(self) -> tuple[float, float, float, float]:
        """Convert the bounding box to a tuple of (x1, y1, x2, y2)."""
        return (
            self.x,
            self.y,
            self.x + self.width,
            self.y + self.height,
        )

    def to_xywh(self) -> tuple[float, float, float, float]:
        """Convert the bounding box to a tuple of (x, y, width, height)."""
        return (
            self.x,
            self.y,
            self.width,
            self.height,
        )
