"""Watermark generation and application."""

import io
from typing import Tuple

from PIL import Image, ImageDraw, ImageFont


class WatermarkEngine:
    """Applies visible and invisible watermarks to media."""

    def __init__(
        self,
        text: str = "AI-GENERATED",
        opacity: float = 0.3,
        position: str = "bottom-right",
        font_size: int = 24,
        invisible: bool = False,
    ):
        self.text = text
        self.opacity = opacity
        self.position = position
        self.font_size = font_size
        self.invisible = invisible

    def apply_to_image(self, image: Image.Image) -> Image.Image:
        """Apply watermark to PIL Image."""

        # Make a copy
        watermarked = image.copy().convert("RGBA")

        # Create watermark layer
        watermark_layer = Image.new("RGBA", watermarked.size, (0, 0, 0, 0))
        draw = ImageDraw.Draw(watermark_layer)

        # Try to load a font, fall back to default
        try:
            font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", self.font_size)
        except:
            try:
                font = ImageFont.truetype("arial.ttf", self.font_size)
            except:
                font = ImageFont.load_default()

        # Get text bounding box
        bbox = draw.textbbox((0, 0), self.text, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]

        # Calculate position
        x, y = self._calculate_position(watermarked.size, (text_width, text_height))

        # Calculate opacity
        alpha = int(255 * self.opacity)

        # Draw visible watermark
        if not self.invisible:
            draw.text(
                (x, y),
                self.text,
                fill=(255, 255, 255, alpha),
                font=font,
            )
            # Add shadow for visibility
            draw.text(
                (x + 2, y + 2),
                self.text,
                fill=(0, 0, 0, int(alpha * 0.5)),
                font=font,
            )

        # Invisible watermark (simple LSB steganography)
        if self.invisible:
            watermarked = self._add_invisible_watermark(watermarked, self.text)

        # Composite
        if not self.invisible:
            watermarked = Image.alpha_composite(watermarked, watermark_layer)

        # Convert back to original mode if needed
        if image.mode != "RGBA":
            watermarked = watermarked.convert(image.mode)

        return watermarked

    def _calculate_position(
        self, image_size: Tuple[int, int], text_size: Tuple[int, int]
    ) -> Tuple[int, int]:
        """Calculate watermark position."""
        img_width, img_height = image_size
        text_width, text_height = text_size
        padding = 20

        positions = {
            "top-left": (padding, padding),
            "top-right": (img_width - text_width - padding, padding),
            "bottom-left": (padding, img_height - text_height - padding),
            "bottom-right": (
                img_width - text_width - padding,
                img_height - text_height - padding,
            ),
            "center": (
                (img_width - text_width) // 2,
                (img_height - text_height) // 2,
            ),
        }

        return positions.get(self.position, positions["bottom-right"])

    def _add_invisible_watermark(self, image: Image.Image, message: str) -> Image.Image:
        """Add invisible watermark using LSB steganography."""

        # Convert message to binary
        binary_message = "".join(format(ord(char), "08b") for char in message)
        binary_message += "00000000"  # Delimiter

        # Get pixel data
        pixels = list(image.getdata())
        new_pixels = []

        message_index = 0
        for pixel in pixels:
            if message_index < len(binary_message):
                # Modify red channel LSB
                r, g, b, a = pixel if len(pixel) == 4 else (*pixel, 255)
                r = (r & 0xFE) | int(binary_message[message_index])
                new_pixels.append((r, g, b, a))
                message_index += 1
            else:
                new_pixels.append(pixel)

        # Create new image
        watermarked = Image.new(image.mode, image.size)
        watermarked.putdata(new_pixels)
        return watermarked
