"""Crisp Windows tray icon rendering for usage percentages (1–100)."""
from __future__ import annotations

import ctypes
import io
import os
import sys
from pathlib import Path
from typing import Any

from PIL import Image, ImageDraw, ImageFont

from colors import percent_color_rgba

BG_COLOR = (30, 33, 38, 255)

_FONT_CANDIDATES = (
    "Bahnschrift SemiBold SemiCondensed.ttf",
    "Bahnschrift SemiCondensed.ttf",
    "Bahnschrift Bold Condensed.ttf",
    "bahnschrift.ttf",
    "bahnsch.ttf",
)


def _fonts_dir() -> Path:
    return Path(os.environ.get("WINDIR", r"C:\Windows")) / "Fonts"


def _find_bahnschrift() -> Path | None:
    fonts = _fonts_dir()
    for name in _FONT_CANDIDATES:
        path = fonts / name
        if path.exists():
            return path
    return None


def _system_dpi() -> int:
    if sys.platform != "win32":
        return 96
    try:
        return int(ctypes.windll.user32.GetDpiForSystem())
    except (AttributeError, OSError):
        return 96


def tray_icon_pixel_size(dpi: int | None = None) -> int:
    dpi = dpi or _system_dpi()
    if dpi >= 192:
        return 32
    if dpi >= 144:
        return 24
    return 16


def _all_icon_sizes(dpi: int | None = None) -> list[int]:
    dpi = dpi or _system_dpi()
    sizes = [16]
    if dpi >= 120:
        sizes.append(20)
    if dpi >= 144:
        sizes.append(24)
    if dpi >= 192:
        sizes.append(32)
    return sorted(set(sizes))


def _label_for_percent(percent: float | None) -> str | None:
    if percent is None:
        return None
    return str(max(0, min(100, round(percent))))


def _load_font(font_path: Path | None, size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
    if font_path is not None:
        try:
            return ImageFont.truetype(str(font_path), size)
        except OSError:
            pass
    return ImageFont.load_default()


def _glyph_mask(
    font: ImageFont.FreeTypeFont | ImageFont.ImageFont,
    text: str,
) -> tuple[Any, int, int, int, int]:
    """Return (mask, offset_x, offset_y, width, height)."""
    if hasattr(font, "getmask2"):
        result = font.getmask2(text, mode="1")
        if result:
            mask, (ox, oy) = result
            tw, th = mask.size
            return mask, ox, oy, tw, th
    mask = font.getmask(text, mode="1")
    bbox = font.getbbox(text)
    return mask, bbox[0], bbox[1], mask.size[0], mask.size[1]


def _ink_bbox(mask) -> tuple[int, int, int, int]:
    tw, th = mask.size
    xs: list[int] = []
    ys: list[int] = []
    for y in range(th):
        for x in range(tw):
            if mask.getpixel((x, y)):
                xs.append(x)
                ys.append(y)
    if not xs:
        return 0, 0, tw - 1, th - 1
    return min(xs), min(ys), max(xs), max(ys)


def _mask_origin_for_ink_center(icon_size: int, mask) -> tuple[int, int]:
    """Top-left mask position so ink pixels are centered on both axes."""
    ix0, iy0, ix1, iy1 = _ink_bbox(mask)
    mx = round(icon_size / 2 - (ix0 + ix1) / 2)
    my = round(icon_size / 2 - (iy0 + iy1) / 2)
    return mx, my


def _mask_fits(icon_size: int, mask) -> bool:
    mx, my = _mask_origin_for_ink_center(icon_size, mask)
    tw, th = mask.size
    ix0, iy0, ix1, iy1 = _ink_bbox(mask)
    if mx < 0 or my < 0 or mx + tw > icon_size or my + th > icon_size:
        return False
    return mx + ix0 >= 0 and my + iy0 >= 0 and mx + ix1 < icon_size and my + iy1 < icon_size


def _find_max_point_size(
    reference: str,
    icon_size: int,
    font_path: Path | None,
    *,
    extra_shrink: int = 0,
) -> int:
    """Largest point size where *reference* text fits centered in the icon."""
    max_pt = icon_size * 2
    for pt in range(max_pt, 3, -1):
        font = _load_font(font_path, pt)
        mask, ox, oy, tw, th = _glyph_mask(font, reference)
        if _mask_fits(icon_size, mask):
            return max(4, pt - extra_shrink)
    return 4


def _fit_font(
    text: str,
    icon_size: int,
    font_path: Path | None,
) -> tuple[ImageFont.FreeTypeFont | ImageFont.ImageFont, Any, int, int, int, int]:
    """
    Largest font whose glyph fits centered in the icon.

    1–99 share one size (fitted to reference '99'); '100' uses its own fit.
    """
    if text == "100":
        pt = _find_max_point_size("100", icon_size, font_path, extra_shrink=1)
    else:
        pt = _find_max_point_size("99", icon_size, font_path)

    font = _load_font(font_path, pt)
    mask, ox, oy, tw, th = _glyph_mask(font, text)
    return font, mask, ox, oy, tw, th


def _mask_to_alpha(mask) -> Image.Image:
    tw, th = mask.size
    pixels = [255 if mask.getpixel((x, y)) else 0 for y in range(th) for x in range(tw)]
    alpha = Image.new("L", (tw, th))
    alpha.putdata(pixels)
    return alpha


def _paste_glyph_centered(
    image: Image.Image,
    mask,
    ox: int,
    oy: int,
    tw: int,
    th: int,
    *,
    smooth: bool,
    font: ImageFont.FreeTypeFont | ImageFont.ImageFont,
    text: str,
    color: tuple[int, int, int, int],
) -> None:
    if smooth:
        draw = ImageDraw.Draw(image)
        cx, cy = image.width // 2, image.height // 2
        draw.text((cx, cy), text, fill=color, font=font, anchor="mm")
        return

    alpha = _mask_to_alpha(mask)
    layer = Image.new("RGBA", (tw, th), color)
    layer.putalpha(alpha)
    mx, my = _mask_origin_for_ink_center(image.width, mask)
    image.alpha_composite(layer, (mx, my))


def _draw_background(image: Image.Image) -> None:
    """Full-bleed square — maximises usable area vs inset circle."""
    draw = ImageDraw.Draw(image)
    s = image.width
    draw.rectangle((0, 0, s - 1, s - 1), fill=BG_COLOR)


def _encode_multi_size_ico(frames: list[Image.Image]) -> Image.Image:
    buffer = io.BytesIO()
    sizes = [(frame.width, frame.height) for frame in frames]
    frames[0].save(buffer, format="ICO", sizes=sizes, append_images=frames[1:])
    buffer.seek(0)
    return Image.open(buffer)


def _render_single_frame(percent: float | None, pixel_size: int) -> Image.Image:
    label = _label_for_percent(percent)
    image = Image.new("RGBA", (pixel_size, pixel_size), (0, 0, 0, 0))

    if label is None:
        draw = ImageDraw.Draw(image)
        _draw_background(image)
        r = pixel_size // 4
        draw.arc(
            (r, r, pixel_size - r, pixel_size - r),
            start=200,
            end=340,
            fill=(200, 205, 212, 255),
            width=max(1, pixel_size // 16),
        )
        draw.arc(
            (r, r, pixel_size - r, pixel_size - r),
            start=20,
            end=160,
            fill=(91, 156, 245, 255),
            width=max(1, pixel_size // 16),
        )
        return image

    _draw_background(image)
    font_path = _find_bahnschrift()
    font, mask, ox, oy, tw, th = _fit_font(label, pixel_size, font_path)
    text_color = percent_color_rgba(min(percent, 100.0))
    _paste_glyph_centered(
        image,
        mask,
        ox,
        oy,
        tw,
        th,
        smooth=pixel_size > 24,
        font=font,
        text=label,
        color=text_color,
    )
    return image


def render_tray_icon(percent: float | None, size: int | None = None) -> Image.Image:
    if sys.platform == "win32" and size is None:
        frames = [_render_single_frame(percent, px) for px in _all_icon_sizes()]
        return _encode_multi_size_ico(frames)
    return _render_single_frame(percent, size or tray_icon_pixel_size())


def render_tray_icon_ico(percent: float | None) -> Image.Image:
    return render_tray_icon(percent)
