#!/usr/bin/env python3
"""Generate Homepage YAML from Homarr default.json with smart categorization."""
import json
import re
from pathlib import Path
from urllib.parse import urlparse

_ROOT = Path(__file__).resolve().parent
HOMARR = _ROOT.parent / "homarr" / "config" / "default.json"
OUT = _ROOT / "config"
WEBSITES_ADD = _ROOT / "websites-add.txt"

ICON_MAP = {
    "adguard": "adguard-home.png",
    "gitea": "gitea.png",
    "grafana": "grafana.png",
    "prometheus": "prometheus.png",
    "portainer": "portainer.png",
    "postgres": "postgres.png",
    "neo4j": "neo4j.png",
    "home assistant": "home-assistant.png",
    "ha ": "home-assistant.png",
    "unifi": "unifi.png",
    "nginx": "nginx-proxy-manager.png",
    "vaultwarden": "vaultwarden.png",
    "immich": "immich.png",
    "n8n": "n8n.png",
    "sonarr": "sonarr.png",
    "radarr": "radarr.png",
    "prowlarr": "prowlarr.png",
    "qbittorrent": "qbittorrent.png",
    "sabnzbd": "sabnzbd.png",
    "nextcloud": "nextcloud.png",
    "excalidraw": "excalidraw.png",
    "wazuh": "wazuh.png",
    "truenas": "truenas.png",
    "frigate": "frigate.png",
    "linkwarden": "linkwarden.png",
    "uptime": "uptime-kuma.png",
    "changedetection": "changedetection.png",
    "traccar": "traccar.png",
    "tunarr": "tunarr.png",
    "metube": "metube.png",
    "remotely": "remotely.png",
    "homarr": "homarr.png",
    "chatgpt": "si-openai",
    "github": "si-github",
    "anthropic": "si-anthropic",
    "deepseek": "si-deepseek",
    "grok": "si-x",
    "youtube": "si-youtube",
    "proxmox": "proxmox.png",
}

# Hex colors per group for mdi/si icons
GROUP_ICON_COLORS = {
    "Infrastructure": "#3b82f6",
    "Media & TV": "#a855f7",
    "Smart Home": "#22c55e",
    "Productivity": "#06b6d4",
    "Tools & Utils": "#f59e0b",
    "AI Assistants": "#ec4899",
    "Dev & Docs": "#14b8a6",
    "Web Design": "#f97316",
}

# Service groups → Homepage tab
SERVICE_GROUPS = [
    "Infrastructure",
    "Media & TV",
    "Smart Home",
    "Productivity",
    "Tools & Utils",
    "AI Assistants",
    "Dev & Docs",
    "Web Design",
]

HOMELAB_RULES = [
    ("Infrastructure", [
        "proxmox", "portainer", "truenas", " adguard", "unifi", "wazuh", "idrac",
        "webmin", "pve script", "modem", "rackula", "mo-nas", "nginx", "nas",
        "lxc", "browserless", "remotely", "minarca", "change detection",
        "changedetection", "dsm on", "dsm aux", "turnkey", "synology stuff",
    ]),
    ("Media & TV", [
        "tunarr", "immich", "frigate", "metube", "nodecast", "nzb geek",
        "usenetdeal",
    ]),
    ("Smart Home", [
        " ha", "home assistant", "ring", "mawaqit", "home control", "ha voice",
    ]),
    ("Productivity", [
        "nextcloud", "vaultwarden", "n8n", "gitea", "excalidraw", "linkwarden",
        "invoice", "onlyoffice", "neo4j", "crm", "resume", "openresume",
        "bentopdf", "vert", "moocup", "thunderbird", "noted apps",
    ]),
]

RSS_BOOKMARKS = {
    "Tech News": [
        ("Hacker News", "https://hnrss.org/frontpage"),
        ("Ars Technica", "https://feeds.arstechnica.com/arstechnica/index"),
        ("The Verge", "https://www.theverge.com/rss/index.xml"),
        ("Lobsters", "https://lobste.rs/rss"),
    ],
    "Security": [
        ("BleepingComputer", "https://www.bleepingcomputer.com/feed/"),
        ("Krebs on Security", "https://krebsonsecurity.com/feed/"),
        ("The Hacker News", "https://feeds.feedburner.com/TheHackersNews"),
    ],
    "Homelab": [
        ("selfh.st", "https://selfh.st/rss/"),
        ("Proxmox Forum", "https://forum.proxmox.com/forums/-/index.rss"),
        ("r/selfhosted", "https://www.reddit.com/r/selfhosted/.rss"),
        ("Docker Blog", "https://www.docker.com/blog/feed/"),
        ("LinuxServer.io", "https://www.linuxserver.io/blog/rss/"),
        ("Marius Hosting", "https://mariushosting.com/feed/"),
    ],
    "AI & Dev": [
        ("OpenAI Blog", "https://openai.com/blog/rss.xml"),
        ("Hugging Face Blog", "https://huggingface.co/blog/feed.xml"),
        ("GitHub Blog", "https://github.blog/feed/"),
    ],
    "Blogs": [
        ("Jack van lightly", "https://jack-vanlightly.com/feed.xml"),
        ("Juhache", "https://juhache.substack.com/feed"),
    ],
}


def slug_name(name: str) -> str:
    s = re.sub(r"[^a-zA-Z0-9]+", "-", name).strip("-").lower()
    return s or "service"


def guess_icon(name: str, url: str) -> str:
    n = name.lower()
    for key, icon in ICON_MAP.items():
        if key in n:
            return icon
    host = urlparse(url).hostname or ""
    if host:
        part = host.split(".")[0]
        if part in ("192", "www"):
            return "mdi-server-network"
        return f"mdi-{part[:12]}"
    return "mdi-circle-outline"


def colorize_icon(icon: str, group: str) -> str:
    """Add hex color to vector icons for vivid Homepage gradients."""
    if "#" in icon or icon.endswith((".png", ".webp", ".svg")) or icon.startswith("http"):
        return icon
    color = GROUP_ICON_COLORS.get(group, "#22d3ee")
    if icon.startswith(("mdi-", "si-")):
        return f"{icon}-{color}"
    return icon


def classify_homelab(name: str, url: str) -> str:
    blob = f" {name.lower()} {url.lower()} "
    for group, keys in HOMELAB_RULES:
        if any(k in blob for k in keys):
            return group
    return "Tools & Utils"


def classify_app(name: str, url: str, homarr_cat: str) -> str:
    if homarr_cat == "AI":
        return "AI Assistants"
    if homarr_cat == "HTML5":
        return "Web Design"
    if homarr_cat in ("Other", "Data Stuff", "Projects"):
        return "Dev & Docs"
    if homarr_cat == "HomeLab Stuff":
        return classify_homelab(name, url)
    return "Tools & Utils"


def yaml_escape(s: str) -> str:
    return s.replace("\\", "\\\\").replace('"', '\\"')


def load_websites_extra() -> dict:
    """Lees websites-add.txt: groep|naam|url per regel."""
    extra = {g: [] for g in SERVICE_GROUPS}
    if not WEBSITES_ADD.exists():
        return extra
    for raw in WEBSITES_ADD.read_text(encoding="utf-8").splitlines():
        line = raw.strip()
        if not line or line.startswith("#"):
            continue
        parts = [p.strip() for p in line.split("|", 2)]
        if len(parts) != 3:
            continue
        group, name, url = parts
        if group not in extra:
            print(f"  Waarschuwing: onbekende groep '{group}' — regel overgeslagen")
            continue
        if name and url:
            extra[group].append((name, url))
    return extra


def merge_groups(base: dict, extra: dict) -> dict:
    """Homarr + handmatige sites; extra overschrijft zelfde naam in zelfde groep."""
    out = {g: list(base.get(g, [])) for g in SERVICE_GROUPS}
    for group, items in extra.items():
        if group not in out:
            out[group] = []
        names = {n.lower() for n, _ in out[group]}
        for name, url in items:
            key = name.lower()
            if key in names:
                out[group] = [(n, u) for n, u in out[group] if n.lower() != key]
                names.discard(key)
            out[group].append((name, url))
            names.add(key)
    return out


def write_services(by_group: dict) -> str:
    lines = []
    for group in SERVICE_GROUPS:
        items = by_group.get(group, [])
        if not items:
            continue
        lines.append(f"- {group}:")
        seen = set()
        for name, url in items:
            key = slug_name(name)
            if key in seen:
                key = f"{key}-{len(seen)}"
            seen.add(key)
            icon = colorize_icon(guess_icon(name, url), group)
            lines.append(f"    - {name}:")
            lines.append(f"        icon: {icon}")
            lines.append(f"        href: {url}")
            lines.append(f"        description: {yaml_escape(name)}")
            try:
                p = urlparse(url)
                if p.scheme and p.hostname and re.match(r"^\d+\.\d+\.\d+\.\d+$", p.hostname):
                    base = f"{p.scheme}://{p.netloc}"
                    lines.append(f"        siteMonitor: {base}")
                    lines.append("        statusStyle: dot")
            except Exception:
                pass
            lines.append("")
    return "\n".join(lines)


def write_settings() -> str:
    layout = {
        "Infrastructure": {"tab": "Ops", "icon": "mdi-server-network", "columns": 4, "style": "row"},
        "Media & TV": {"tab": "Media", "icon": "mdi-television-play", "columns": 4, "style": "row"},
        "Smart Home": {"tab": "Home", "icon": "mdi-home-automation", "columns": 3, "style": "row"},
        "Productivity": {"tab": "Work", "icon": "mdi-briefcase-outline", "columns": 4, "style": "row"},
        "Tools & Utils": {"tab": "Ops", "icon": "mdi-toolbox-outline", "columns": 3, "style": "row"},
        "AI Assistants": {"tab": "AI", "icon": "mdi-brain", "columns": 4, "style": "row"},
        "Dev & Docs": {"tab": "AI", "icon": "mdi-book-open-page-variant", "columns": 3, "style": "row"},
        "Web Design": {"tab": "AI", "icon": "mdi-palette-outline", "columns": 3, "style": "row"},
        "Tech News": {"tab": "Feeds", "icon": "mdi-newspaper-variant-outline", "columns": 2},
        "Security": {"tab": "Feeds", "icon": "mdi-shield-alert-outline", "columns": 2},
        "Homelab": {"tab": "Feeds", "icon": "mdi-server", "columns": 3},
        "AI & Dev": {"tab": "Feeds", "icon": "mdi-robot-outline", "columns": 2},
        "Blogs": {"tab": "Feeds", "icon": "mdi-rss", "columns": 2},
    }
    lines = [
        "---",
        "title: EL-KADI OPS",
        "description: Homelab command surface",
        "theme: dark",
        "color: slate",
        "iconStyle: gradient",
        "headerStyle: underlined",
        "useEqualHeights: true",
        "statusStyle: dot",
        "fullWidth: true",
        "cardBlur: md",
        "hideVersion: false",
        "quicklaunch:",
        "  searchDescriptions: true",
        "  hideInternetSearch: false",
        "  showSearchSuggestions: true",
        "layout:",
    ]
    for name, opts in layout.items():
        lines.append(f"  {name}:")
        for k, v in opts.items():
            lines.append(f"    {k}: {v}")
    return "\n".join(lines) + "\n"


def write_bookmarks() -> str:
    lines = ["---"]
    for group, items in RSS_BOOKMARKS.items():
        lines.append(f"- {group}:")
        for name, url in items:
            abbr = "".join(w[0] for w in re.findall(r"[A-Za-z0-9]+", name)[:2]).upper()[:2] or "RS"
            lines.append(f"    - {name}:")
            lines.append(f"        - abbr: {abbr}")
            lines.append(f"          href: {url}")
    return "\n".join(lines) + "\n"


def write_widgets() -> str:
    return """---
- logo:
    icon: /images/logo.jpg

- greeting:
    text_size: xl
    text: el-kadi ops

- datetime:
    text_size: xl
    format:
      timeStyle: short
      dateStyle: medium
      hour12: false

- search:
    provider: google
    target: _blank
    showSearchSuggestions: true

- resources:
    cpu: true
    memory: true
    cputemp: true
    uptime: true
    units: metric
    refresh: 3000
"""


def main():
    with open(HOMARR) as f:
        d = json.load(f)
    cats = {c["id"]: c["name"] for c in d.get("categories", [])}
    by_group: dict[str, list] = {g: [] for g in SERVICE_GROUPS}

    for a in d.get("apps", []):
        name = (a.get("name") or "").strip()
        url = (a.get("url") or "").strip()
        if not name or not url:
            continue
        area = a.get("area", {}).get("properties", {}).get("id", "")
        homarr_cat = cats.get(area, "Other")
        group = classify_app(name, url, homarr_cat)
        by_group.setdefault(group, []).append((name, url))

    extra = load_websites_extra()
    extra_count = sum(len(v) for v in extra.values())
    by_group = merge_groups(by_group, extra)

    OUT.mkdir(parents=True, exist_ok=True)
    (OUT / "services.yaml").write_text(write_services(by_group))
    (OUT / "settings.yaml").write_text(write_settings())
    (OUT / "widgets.yaml").write_text(write_widgets())
    (OUT / "bookmarks.yaml").write_text(write_bookmarks())

    total = sum(len(v) for v in by_group.values())
    print(
        f"Generated {total} apps in {sum(1 for v in by_group.values() if v)} groups"
        + (f" (+{extra_count} uit websites-add.txt)" if extra_count else "")
    )


if __name__ == "__main__":
    main()
