# edited by glg

import os
import re
from typing import Dict, List, Tuple

from PySide6.QtCore import Qt
from PySide6.QtGui import QPixmap


_SUPPORTED_EXTS = {".png", ".jpg", ".jpeg", ".bmp", ".ico", ".svg"}
_BITMAP_EXTS = {".png", ".jpg", ".jpeg", ".bmp", ".ico"}


def _normalize_base_stem(stem: str) -> str:
    text = str(stem or "").strip()
    return re.sub(r"@(?:2|3|4)x$", "", text, flags=re.IGNORECASE).lower()


def _is_variant_stem(stem: str) -> bool:
    return bool(re.search(r"@(?:2|3|4)x$", str(stem or ""), flags=re.IGNORECASE))


def find_related_image_files(image_path: str) -> List[str]:
    path = str(image_path or "").strip()
    if not path:
        return []
    folder = os.path.dirname(path)
    if not folder or not os.path.isdir(folder):
        return [path] if os.path.exists(path) else []
    filename = os.path.basename(path)
    stem, _ext = os.path.splitext(filename)
    base_stem = _normalize_base_stem(stem)
    candidates = []
    for name in os.listdir(folder):
        full = os.path.join(folder, name)
        if not os.path.isfile(full):
            continue
        file_stem, file_ext = os.path.splitext(name)
        if str(file_ext or "").lower() not in _SUPPORTED_EXTS:
            continue
        if _normalize_base_stem(file_stem) != base_stem:
            continue
        candidates.append(full)
    if os.path.exists(path) and path not in candidates:
        candidates.insert(0, path)
    candidates.sort(key=lambda p: str(os.path.splitext(p)[1] or "").lower())
    return candidates


def _render_svg(svg_path: str, target_w: int, target_h: int) -> QPixmap:
    try:
        from PySide6.QtSvg import QSvgRenderer  # type: ignore
    except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, ImportError):
        return QPixmap()
    renderer = QSvgRenderer(svg_path)
    if not renderer.isValid():
        return QPixmap()
    pixmap = QPixmap(max(1, int(target_w)), max(1, int(target_h)))
    pixmap.fill(Qt.transparent)
    from PySide6.QtGui import QPainter
    painter = QPainter(pixmap)
    renderer.render(painter)
    painter.end()
    return pixmap


def load_best_scaled_pixmap(
    image_path: str,
    target_w: int,
    target_h: int,
    *,
    keep_aspect: bool = True,
    smooth: bool = True,
    prefer_svg: bool = True,
) -> QPixmap:
    paths = find_related_image_files(image_path)
    if not paths:
        return QPixmap()
    safe_w = max(1, int(target_w or 1))
    safe_h = max(1, int(target_h or 1))
    aspect_mode = Qt.KeepAspectRatio if keep_aspect else Qt.IgnoreAspectRatio
    transform_mode = Qt.SmoothTransformation if smooth else Qt.FastTransformation

    svg_candidates = [p for p in paths if os.path.splitext(p)[1].lower() == ".svg"]
    if prefer_svg and svg_candidates:
        svg_pixmap = _render_svg(svg_candidates[0], safe_w, safe_h)
        if not svg_pixmap.isNull():
            return svg_pixmap.scaled(safe_w, safe_h, aspect_mode, transform_mode)

    bitmap_candidates = [p for p in paths if os.path.splitext(p)[1].lower() in _BITMAP_EXTS]
    best_pixmap = QPixmap()
    best_area = -1
    for candidate in bitmap_candidates:
        pixmap = QPixmap(candidate)
        if pixmap.isNull():
            continue
        area = int(pixmap.width()) * int(pixmap.height())
        if area > best_area:
            best_area = area
            best_pixmap = pixmap
    if best_pixmap.isNull() and not prefer_svg and svg_candidates:
        best_pixmap = _render_svg(svg_candidates[0], safe_w, safe_h)
    if best_pixmap.isNull():
        return QPixmap()
    return best_pixmap.scaled(safe_w, safe_h, aspect_mode, transform_mode)


def load_best_source_pixmap(
    image_path: str,
    *,
    prefer_svg: bool = True,
    svg_render_size: Tuple[int, int] = (512, 512),
) -> QPixmap:
    paths = find_related_image_files(image_path)
    if not paths:
        return QPixmap()
    svg_candidates = [p for p in paths if os.path.splitext(p)[1].lower() == ".svg"]
    if prefer_svg and svg_candidates:
        try:
            sw = max(1, int(svg_render_size[0]))
            sh = max(1, int(svg_render_size[1]))
        except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError):
            sw, sh = 512, 512
        svg_pixmap = _render_svg(svg_candidates[0], sw, sh)
        if not svg_pixmap.isNull():
            return svg_pixmap
    bitmap_candidates = [p for p in paths if os.path.splitext(p)[1].lower() in _BITMAP_EXTS]
    best_pixmap = QPixmap()
    best_area = -1
    for candidate in bitmap_candidates:
        pixmap = QPixmap(candidate)
        if pixmap.isNull():
            continue
        area = int(pixmap.width()) * int(pixmap.height())
        if area > best_area:
            best_area = area
            best_pixmap = pixmap
    if best_pixmap.isNull() and not prefer_svg and svg_candidates:
        return _render_svg(svg_candidates[0], 512, 512)
    return best_pixmap


def audit_image_variant_map(asset_dir: str) -> Dict[str, object]:
    root = str(asset_dir or "").strip()
    if not root or not os.path.isdir(root):
        return {
            "root": root,
            "total_assets": 0,
            "svg_ready": 0,
            "has_2x": 0,
            "has_3x": 0,
            "missing_svg": [],
            "missing_2x": [],
            "missing_3x": [],
        }
    all_files = []
    for name in os.listdir(root):
        full = os.path.join(root, name)
        if not os.path.isfile(full):
            continue
        stem, ext = os.path.splitext(name)
        if str(ext or "").lower() not in _SUPPORTED_EXTS:
            continue
        all_files.append((name, stem, ext.lower(), full))

    base_files = []
    for _name, stem, ext, full in all_files:
        if _is_variant_stem(stem):
            continue
        if ext not in _BITMAP_EXTS:
            continue
        base_files.append((stem, ext, full))

    missing_svg = []
    missing_2x = []
    missing_3x = []
    svg_ready = 0
    has_2x = 0
    has_3x = 0
    for stem, _ext, _full in base_files:
        base = _normalize_base_stem(stem)
        related = [entry for entry in all_files if _normalize_base_stem(entry[1]) == base]
        exts = {entry[2] for entry in related}
        stems = {entry[1].lower() for entry in related}
        if ".svg" in exts:
            svg_ready += 1
        else:
            missing_svg.append(stem)
        if any(str(s).endswith("@2x") for s in stems):
            has_2x += 1
        else:
            missing_2x.append(stem)
        if any(str(s).endswith("@3x") for s in stems):
            has_3x += 1
        else:
            missing_3x.append(stem)

    return {
        "root": root,
        "total_assets": len(base_files),
        "svg_ready": svg_ready,
        "has_2x": has_2x,
        "has_3x": has_3x,
        "missing_svg": sorted(missing_svg),
        "missing_2x": sorted(missing_2x),
        "missing_3x": sorted(missing_3x),
    }
