#!/usr/bin/python3
"""
Scan source code for deprecated Adwaita symbolic icons and optionally replace them.
"""

import os
import re
import argparse

# Meson sets this at build time; fall back safely for system installs
XAPP_DATA_DIR = '/usr/share/xapp'
if not os.path.exists(XAPP_DATA_DIR):
    XAPP_DATA_DIR = '/usr/share/xapp'

EXCLUDED_DIRS = {
    ".git",
    "build",
    "__pycache__",
    "LC_MESSAGES",
    "debian",
    "obj-x86_64-linux-gnu",
    "tmp",
}
EXCLUDED_EXTENSIONS = {"svg", "pot", "po", "mo"}

# -----------------------------------------------------------------------------
# CLI
# -----------------------------------------------------------------------------
def parse_args():
    parser = argparse.ArgumentParser(
        description="Scan source code for deprecated Adwaita symbolic icons and suggest or apply replacements."
    )
    parser.add_argument(
        "path",
        nargs="?",
        default=".",
        help="Path to the directory to scan (default: current directory).",
    )
    parser.add_argument(
        "--fix",
        action="store_true",
        help="Automatically replace deprecated icons (exact -symbolic matches only) with xsi-<new>-symbolic.",
    )
    return parser.parse_args()


# -----------------------------------------------------------------------------
# Icon loading and regex setup
# -----------------------------------------------------------------------------
def load_icon_map():
    icon_map = {}
    datafile = os.path.join(XAPP_DATA_DIR, "xsi-adwaita-symbolic.info")
    with open(datafile, "r", encoding="utf-8") as flist:
        for line in flist:
            line = line.strip()
            if not line or line.startswith("#"):
                continue
            old, cat, new = line.split(" # ")
            icon_map[old] = new
    return icon_map


def build_patterns(icon_map):
    # Custom word boundaries that allow hyphens
    def boundary_join(names):
        return r'(?<![A-Za-z0-9_-])(' + '|'.join(re.escape(n) for n in names) + r')(?![A-Za-z0-9_-])'

    symbolic_pattern = re.compile(boundary_join(name + "-symbolic" for name in icon_map))
    bare_pattern = re.compile(boundary_join(icon_map.keys()))
    context_pattern = re.compile(
        r'icon_name|set_icon_name|set_from_icon_name|from_icon_name|icon=|icon-name',
        re.IGNORECASE,
    )
    return symbolic_pattern, bare_pattern, context_pattern

# -----------------------------------------------------------------------------
# Core logic
# -----------------------------------------------------------------------------
def scan_and_fix(base_path, icon_map, symbolic_pattern, bare_pattern, context_pattern, fix=False):
    exact_hits = []
    context_hits = []

    for root, dirs, files in os.walk(base_path):
        dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS]

        for fname in files:
            if fname.split(".")[-1].lower() in EXCLUDED_EXTENSIONS:
                continue

            full_path = os.path.join(root, fname)
            try:
                with open(full_path, "r", encoding="utf-8", errors="ignore") as f:
                    lines = f.readlines()
            except Exception:
                continue

            modified = False

            for lineno, line in enumerate(lines):
                # Exact (-symbolic) matches
                found_symbolic = symbolic_pattern.findall(line)

                # If the line already matched an exact one, skip context search entirely
                if found_symbolic:
                    found_bare = []
                else:
                    # Contextual bare matches
                    found_bare = bare_pattern.findall(line) if context_pattern.search(line) else []

                # --- Record exact matches ---
                for name in found_symbolic:
                    base = name[:-9]
                    suggestion = icon_map.get(base, "")
                    msg = f"{full_path}:{lineno + 1}: {line.strip()}  → xsi-{suggestion}-symbolic"
                    exact_hits.append(msg)

                    if fix and suggestion:
                        new_icon = f"xsi-{suggestion}-symbolic"
                        lines[lineno] = re.sub(re.escape(name), new_icon, lines[lineno])
                        modified = True

                # --- Record contextual matches (not auto-fixed) ---
                for name in found_bare:
                    suggestion = icon_map.get(name, "")
                    msg = f"{full_path}:{lineno + 1}: {line.strip()}  → xsi-{suggestion}-symbolic  (context)"
                    context_hits.append(msg)

            if fix and modified:
                try:
                    with open(full_path, "w", encoding="utf-8") as f:
                        f.writelines(lines)
                except Exception as e:
                    print(f"⚠️ Could not modify {full_path}: {e}")

    # --- Reporting ---
    print("\n=== Exact matches (can be fixed) ===")
    for msg in exact_hits:
        print(msg)

    print("\n=== Context matches (manual review) ===")
    for msg in context_hits:
        print(msg)

    print(f"\nScan complete — {len(exact_hits)} exact matches, {len(context_hits)} contextual matches found.")
    if fix and exact_hits:
        print("Files were modified to apply xsi-<new>-symbolic replacements.")


# -----------------------------------------------------------------------------
# Entry point
# -----------------------------------------------------------------------------
def main():
    args = parse_args()
    icon_map = load_icon_map()
    symbolic_pattern, bare_pattern, context_pattern = build_patterns(icon_map)
    scan_and_fix(args.path, icon_map, symbolic_pattern, bare_pattern, context_pattern, fix=args.fix)


if __name__ == "__main__":
    main()
