import os
import urllib.error
import urllib.request

import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw, Gio

import stillCenter.AppStore as AppStore
import stillCenter.UrlImage as UrlImage

import stillCenter.constants as constants

@Gtk.Template(filename=os.path.join(constants.UI_DIR, "FlowAppButton.ui"))
class FlowAppButton(Gtk.Button):
    __gtype_name__ = "FlowAppButton"

    name_label: Gtk.Label = Gtk.Template.Child()
    author_label: Gtk.Label = Gtk.Template.Child()
    icon: UrlImage.UrlImage = Gtk.Template.Child()

    def __init__(self, flow_apps, app_id: str, name: str, author: str, icon: str):
        super().__init__()
        self.flow_apps = flow_apps
        self.app_id = app_id
        self.name_label.set_label(name)
        if author is None:
            self.author_label.set_visible(False)
        else:
            self.author_label.set_visible(True)
            self.author_label.set_label(author)
        self.icon.set_image_url(app_id, icon)
        self.connect("clicked", self.on_click)

    def on_click(self, *args, **kwargs):
        self.flow_apps.stillCenter.app_page.show_app(self.app_id)


@Gtk.Template(filename=os.path.join(constants.UI_DIR, "FlowApps.ui"))
class FlowApps(Gtk.Box):
    __gtype_name__ = "FlowApps"

    label = Gtk.Template.Child()
    flowbox = Gtk.Template.Child()
    stillCenter = None

    def __init__(self):
        super().__init__()

    def set_section(self, stillCenter, title: str, app_ids: list[str], app_items_by_id: dict):
        self.stillCenter = stillCenter
        self.label.set_label(title)
        self.flowbox.remove_all()

        for app_id in app_ids:
            item = app_items_by_id.get(app_id)
            if item is None:
                continue
            self.flowbox.append(
                FlowAppButton(self, item.app_id, item.name, item.author, item.icon)
            )

        self.set_visible(self.flowbox.get_first_child() is not None)


def _strip_yaml_value(value: str) -> str:
    value = value.strip()
    if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")):
        return value[1:-1]
    return value


def _normalize_sections(data) -> list[dict]:
    if data is None:
        return []

    if isinstance(data, dict) and "sections" in data:
        data = data["sections"]

    sections = []

    if isinstance(data, list):
        raw_sections = data
    elif isinstance(data, dict):
        # Support map style: { "Section Name": [ids...] }
        raw_sections = [{"name": key, "app_ids": value} for key, value in data.items()]
    else:
        return []

    for section in raw_sections:
        if not isinstance(section, dict):
            continue

        name = section.get("name") or section.get("title")
        ids = (
            section.get("app_ids")
            or section.get("apps")
            or section.get("ids")
            or section.get("sadb_ids")
            or []
        )
        if not isinstance(name, str):
            continue
        if not isinstance(ids, list):
            continue

        cleaned_ids = [str(app_id).strip() for app_id in ids if str(app_id).strip()]
        if cleaned_ids:
            sections.append({"name": name.strip(), "app_ids": cleaned_ids})

    return sections


def _parse_yaml_fallback(text: str) -> list[dict]:
    # Minimal parser for a simple list-of-sections YAML format.
    sections = []
    current = None
    reading_ids = False
    ids_indent = 0

    for raw_line in text.splitlines():
        line = raw_line.split("#", 1)[0].rstrip()
        if not line.strip():
            continue

        indent = len(line) - len(line.lstrip(" "))
        stripped = line.strip()

        if stripped.startswith("- name:"):
            if current and current["app_ids"]:
                sections.append(current)
            current = {"name": _strip_yaml_value(stripped.split(":", 1)[1]), "app_ids": []}
            reading_ids = False
            continue

        if stripped.startswith("name:"):
            if current is None:
                current = {"name": "", "app_ids": []}
            current["name"] = _strip_yaml_value(stripped.split(":", 1)[1])
            reading_ids = False
            continue

        if any(stripped.startswith(key) for key in ("app_ids:", "apps:", "ids:", "sadb_ids:")):
            value = stripped.split(":", 1)[1].strip()
            reading_ids = True
            ids_indent = indent
            if value.startswith("[") and value.endswith("]"):
                inline = [entry.strip() for entry in value[1:-1].split(",")]
                for app_id in inline:
                    app_id = _strip_yaml_value(app_id)
                    if app_id:
                        if current is None:
                            current = {"name": "Featured", "app_ids": []}
                        current["app_ids"].append(app_id)
                reading_ids = False
            continue

        if reading_ids and stripped.startswith("-"):
            app_id = _strip_yaml_value(stripped[1:].strip())
            if app_id:
                if current is None:
                    current = {"name": "Featured", "app_ids": []}
                current["app_ids"].append(app_id)
            continue

        if reading_ids and indent <= ids_indent and ":" in stripped and not stripped.startswith("-"):
            reading_ids = False

    if current and current["app_ids"]:
        sections.append(current)

    return sections


def _load_featured_sections() -> list[dict]:
    yaml_text = None
    url = _get_featured_apps_url()
    try:
        with urllib.request.urlopen(url, timeout=8) as response:
            yaml_text = response.read().decode("utf-8")
    except (urllib.error.URLError, TimeoutError, ValueError):
        return []

    if not yaml_text:
        return []

    try:
        import yaml  # type: ignore

        return _normalize_sections(yaml.safe_load(yaml_text))
    except Exception:
        return _parse_yaml_fallback(yaml_text)


def _get_featured_apps_url() -> str:
    default_url = constants.FEATURED_APPS_URL
    try:
        schema_source = Gio.SettingsSchemaSource.get_default()
        if schema_source is None:
            return default_url

        schema = schema_source.lookup("io.stillhq.stillCenter", True)
        if schema is None:
            return default_url

        settings = Gio.Settings.new_full(schema, None, None)
        configured_url = settings.get_string("featured-apps-url").strip()
        return configured_url or default_url
    except Exception:
        return default_url


def _fallback_sections_from_tags() -> list[dict]:
    legacy_sections = [
        ("Essentials", "keyword-essentials"),
        ("Grab your favorite Web Browser!", "WebBrowser"),
        ("Used for stillOS", "keyword-usedforstillos"),
    ]
    sections = []
    for name, tag in legacy_sections:
        store = AppStore.STORE.get(tag)
        if store is None:
            continue
        app_ids = []
        for i in range(min(8, store.get_n_items())):
            app_ids.append(store.get_item(i).app_id)
        if app_ids:
            sections.append({"name": name, "app_ids": app_ids})
    return sections


def populate_apps(home_sections: Gtk.Box, stillCenter):
    child = home_sections.get_first_child()
    while child is not None:
        next_child = child.get_next_sibling()
        home_sections.remove(child)
        child = next_child

    all_store = AppStore.STORE.get("all")
    if all_store is None:
        return

    app_items_by_id = {}
    for i in range(all_store.get_n_items()):
        item = all_store.get_item(i)
        app_items_by_id[item.app_id] = item

    sections = _load_featured_sections()
    if not sections:
        sections = _fallback_sections_from_tags()

    for section in sections:
        widget = FlowApps()
        widget.set_section(stillCenter, section["name"], section["app_ids"], app_items_by_id)
        if widget.get_visible():
            home_sections.append(widget)