"""Color selection utilities."""

from __future__ import annotations

import colorsys

from tkinter import colorchooser, messagebox


class ColorPickerMixin:
    """Handles colour selection from dialogs and mouse clicks."""

    ref_hue: float | None
    hue_span: float = 45.0  # degrees around the picked hue
    selected_colour: tuple[int, int, int] | None = None

    def choose_color(self):
        title = self._t("dialog.choose_colour_title") if hasattr(self, "_t") else "Choose colour"
        rgb, hex_colour = colorchooser.askcolor(title=title)
        if rgb is None:
            return
        r, g, b = (int(round(channel)) for channel in rgb)
        hue_deg, sat_pct, val_pct = self._apply_rgb_selection(r, g, b)
        label = hex_colour or f"RGB({r}, {g}, {b})"
        message = self._t(
            "status.color_selected",
            label=label,
            hue=hue_deg,
            saturation=sat_pct,
            value=val_pct,
        )
        self.status.config(text=message)
        self._update_selected_colour(r, g, b)

    def apply_sample_colour(self, hex_colour: str, name: str | None = None) -> None:
        """Apply a predefined colour preset."""
        rgb = self._parse_hex_colour(hex_colour)
        if rgb is None:
            return
        hue_deg, sat_pct, val_pct = self._apply_rgb_selection(*rgb)
        if self.pick_mode:
            self.pick_mode = False
        label = name or hex_colour.upper()
        message = self._t(
            "status.sample_colour",
            label=label,
            hex_code=hex_colour,
            hue=hue_deg,
            saturation=sat_pct,
            value=val_pct,
        )
        self.status.config(text=message)
        self._update_selected_colour(*rgb)

    def enable_pick_mode(self):
        if self.preview_img is None:
            messagebox.showinfo(
                self._t("dialog.info_title"),
                self._t("dialog.load_image_first"),
            )
            return
        self.pick_mode = True
        self.status.config(text=self._t("status.pick_mode_ready"))

    def disable_pick_mode(self, event=None):
        if self.pick_mode:
            self.pick_mode = False
            self.status.config(text=self._t("status.pick_mode_ended"))

    def on_canvas_click(self, event):
        if not self.pick_mode or self.preview_img is None:
            return
        x = int(event.x)
        y = int(event.y)
        if x < 0 or y < 0 or x >= self.preview_img.width or y >= self.preview_img.height:
            return
        r, g, b, a = self.preview_img.getpixel((x, y))
        if a == 0:
            return
        hue_deg, sat_pct, val_pct = self._apply_rgb_selection(r, g, b)
        self.disable_pick_mode()
        self.status.config(
            text=self._t(
                "status.pick_mode_from_image",
                hue=hue_deg,
                saturation=sat_pct,
                value=val_pct,
            )
        )
        self._update_selected_colour(r, g, b)

    def _apply_rgb_selection(self, r: int, g: int, b: int) -> tuple[float, float, float]:
        """Update slider ranges based on an RGB colour and return HSV summary."""
        h, s, v = colorsys.rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0)
        hue_deg = (h * 360.0) % 360.0
        self.ref_hue = hue_deg
        self._set_slider_targets(hue_deg, s, v)
        self.update_preview()
        return hue_deg, s * 100.0, v * 100.0

    def _update_selected_colour(self, r: int, g: int, b: int) -> None:
        self.selected_colour = (r, g, b)
        hex_colour = f"#{r:02x}{g:02x}{b:02x}"
        if hasattr(self, "current_colour_sw"):
            try:
                self.current_colour_sw.configure(background=hex_colour)
            except Exception:
                pass
        if hasattr(self, "current_colour_label"):
            try:
                self.current_colour_label.configure(text=f"({hex_colour})")
            except Exception:
                pass

    def _set_slider_targets(self, hue_deg: float, saturation: float, value: float) -> None:
        span = getattr(self, "hue_span", 45.0)
        self.hue_min.set((hue_deg - span) % 360)
        self.hue_max.set((hue_deg + span) % 360)

        sat_pct = saturation * 100.0
        sat_margin = 35.0
        sat_min = max(0.0, min(100.0, sat_pct - sat_margin))
        if saturation <= 0.05:
            sat_min = 0.0
        self.sat_min.set(sat_min)

        v_pct = value * 100.0
        val_margin = 35.0
        val_min = max(0.0, v_pct - val_margin)
        val_max = min(100.0, v_pct + val_margin)
        if value <= 0.15:
            val_max = min(45.0, max(val_max, 25.0))
        if value >= 0.85:
            val_min = max(55.0, min(val_min, 80.0))
        if val_max <= val_min:
            val_max = min(100.0, val_min + 10.0)
        self.val_min.set(val_min)
        self.val_max.set(val_max)

    @staticmethod
    def _parse_hex_colour(hex_colour: str | None) -> tuple[int, int, int] | None:
        if not hex_colour:
            return None
        value = hex_colour.strip().lstrip("#")
        if len(value) == 3:
            value = "".join(ch * 2 for ch in value)
        if len(value) != 6:
            return None
        try:
            r = int(value[0:2], 16)
            g = int(value[2:4], 16)
            b = int(value[4:6], 16)
        except ValueError:
            return None
        return r, g, b


__all__ = ["ColorPickerMixin"]
