#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf, Pango
import os
import subprocess
import sys
import glob

FLAG_FILE = os.path.expanduser("~/.config/basalt-welcome.done")
VERSION = "1.2 Obsidian"
WP_DIR = "/usr/share/backgrounds"
THEME_DIR = "/usr/share/themes"
ICON_DIR = "/usr/share/icons"
AC = "#05b49e"


def get_cursors():
    items = []
    for d in sorted(os.listdir(ICON_DIR)):
        if os.path.isfile(os.path.join(ICON_DIR, d, "index.theme")) and \
           os.path.isdir(os.path.join(ICON_DIR, d, "cursors")):
            items.append(d)
    for p in ["Bibata-Modern-Ice", "Bibata-Modern-Classic", "Bibata-Original-Ice", "Adwaita"]:
        if p in items:
            return dedup_ordered([p] + items)[:5]
    return items[:5] or ["Adwaita"]


def get_themes():
    items = [d for d in sorted(os.listdir(THEME_DIR))
             if os.path.isfile(os.path.join(THEME_DIR, d, "index.theme"))]
    for p in ["Orchis", "Orchis-Dark", "Adwaita", "Adwaita-dark"]:
        if p in items:
            return sorted([x for x in items if x in ["Orchis", "Orchis-Dark", "Adwaita", "Adwaita-dark"]],
                          key=lambda x: ["Orchis", "Orchis-Dark", "Adwaita", "Adwaita-dark"].index(x))
    return items[:5]


def dedup_ordered(seq):
    seen = set()
    return [x for x in seq if not (x in seen or seen.add(x))]

def get_icons():
    ICON_SIZES = {"16x16", "22x22", "24x24", "32x32", "48x48",
                  "64x64", "96x96", "128x128", "scalable", "symbolic"}
    items = []
    for d in sorted(os.listdir(ICON_DIR)):
        dp = os.path.join(ICON_DIR, d)
        if d in ("default", "vendor") or not os.path.isfile(os.path.join(dp, "index.theme")):
            continue
        subdirs = set(os.listdir(dp))
        if not subdirs & ICON_SIZES:
            continue
        items.append(d)
    for p in ["Papirus", "Papirus-Dark", "Adwaita"]:
        if p in items:
            return dedup_ordered([p] + items)[:5]
    return items[:5]


def get_wallpapers():
    files = sorted(glob.glob(os.path.join(WP_DIR, "basalt-desktop-*.jpg")))
    return [(f, os.path.basename(f).replace(".jpg", "").replace("basalt-desktop-", ""))
            for f in files]


def xset(channel, prop, value, vtype="string"):
    cmd = ["xfconf-query", "-c", channel, "-p", prop, "-s", str(value), "--create"]
    if vtype in ("int", "bool"):
        cmd += ["-t", vtype]
    subprocess.run(cmd, capture_output=True, text=True)


def set_wallpaper(fp):
    if not os.path.exists(fp):
        return
    r = subprocess.run(["xfconf-query", "-c", "xfce4-desktop", "-l"],
                       capture_output=True, text=True)
    for line in r.stdout.splitlines():
        if line.endswith("/last-image") or line.endswith("/image-path"):
            xset("xfce4-desktop", line, fp)
        if line == "/backdrop/last-single-image":
            xset("xfce4-desktop", line, fp)
    subprocess.run(["xfdesktop", "--reload"], capture_output=True)


CURSORS, THEMES, ICONS, WALLPAPERS = get_cursors(), get_themes(), get_icons(), get_wallpapers()
GSETTINGS = Gtk.Settings.get_default()


class HubButton(Gtk.EventBox):
    def __init__(self, label, desc, icon_name, page_idx, callback):
        super().__init__()
        self.page_idx = page_idx
        self.callback = callback
        self.set_margin_bottom(8)

        card = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
        card.set_margin_start(16)
        card.set_margin_end(16)
        card.set_margin_top(14)
        card.set_margin_bottom(14)
        card.get_style_context().add_class("hub-card")

        ico = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DIALOG)

        vb = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
        tl = Gtk.Label()
        tl.set_markup(f"<b>{label}</b>")
        tl.set_xalign(0.0)
        vb.pack_start(tl, False, False, 0)
        dl = Gtk.Label()
        dl.set_text(desc)
        dl.set_xalign(0.0)
        dl.get_style_context().add_class("hub-desc")
        vb.pack_start(dl, False, False, 0)

        card.pack_start(ico, False, False, 0)
        card.pack_start(vb, True, True, 0)

        arrow = Gtk.Label()
        arrow.set_markup("<span size='large' foreground='#05b49e'>\u2192</span>")
        card.pack_start(arrow, False, False, 0)

        self.add(card)
        self.connect("button-press-event", self.on_click)

    def on_click(self, w, e):
        self.callback(self.page_idx)


class BasaltWelcome(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Basalt Linux Setup")
        self.set_icon_name("basalt-logo")
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_default_size(680, 520)
        self.set_resizable(True)
        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)

        css = b"""
        .hub-card {
            background-color: rgba(255,255,255,0.04);
            border-radius: 10px;
            border: 1px solid rgba(255,255,255,0.08);
        }
        .hub-card:hover { background-color: rgba(5,180,158,0.08); border-color: """ + AC.encode() + b"""; }
        .hub-desc { font-size: 11px; color: #8899aa; }
        .section-label { font-size: 10px; color: #667; letter-spacing: 1px; font-weight: bold; }
        .subtitle-text { color: #8899aa; font-size: 12px; }
        .feature-desc { font-size: 11px; color: #8899aa; }
        .primary-btn { background-color: """ + AC.encode() + b"""; color: #fff; border: none; font-weight: bold; border-radius: 6px; padding: 7px 22px; }
        .primary-btn:hover { background-color: #04a088; }
        .secondary-btn { background: none; border: 1px solid #3a4060; color: #ccc; border-radius: 6px; padding: 7px 22px; }
        .secondary-btn:hover { background-color: rgba(255,255,255,0.05); }
        """
        prov = Gtk.CssProvider()
        prov.load_from_data(css)
        screen = Gdk.Display.get_default().get_default_screen()
        Gtk.StyleContext.add_provider_for_screen(
            screen, prov, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

        self.current_page = 0
        self.total_pages = 4
        self.sw_setup = {}

        vb = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        self.add(vb)

        # Header
        hdr = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        hdr.set_margin_start(20)
        hdr.set_margin_end(20)
        hdr.set_margin_top(14)
        hdr.set_margin_bottom(6)

        logo_p = "/usr/share/icons/hicolor/scalable/apps/basalt-logo.svg"
        if os.path.exists(logo_p):
            try:
                pb = GdkPixbuf.Pixbuf.new_from_file_at_size(logo_p, 26, 26)
                hdr.pack_start(Gtk.Image.new_from_pixbuf(pb), False, False, 0)
            except Exception:
                pass
        tl = Gtk.Label()
        tl.set_markup("<b>  Basalt Linux</b>")
        hdr.pack_start(tl, False, False, 0)
        hdr.pack_start(Gtk.Label(), True, True, 0)

        self.step_lbl = Gtk.Label()
        self.step_lbl.get_style_context().add_class("subtitle-text")
        hdr.pack_start(self.step_lbl, False, False, 0)
        vb.pack_start(hdr, False, False, 0)

        # Content
        self.stack = Gtk.Stack()
        self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
        self.stack.set_transition_duration(200)
        self.stack.set_margin_start(24)
        self.stack.set_margin_end(24)
        self.stack.set_margin_top(4)
        self.stack.set_margin_bottom(4)
        vb.pack_start(self.stack, True, True, 0)

        self.build_pages()

        # Bottom
        bot = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
        bot.set_margin_start(16)
        bot.set_margin_end(16)
        bot.set_margin_bottom(12)
        bot.set_margin_top(4)

        self.cls = Gtk.Button(label="Close")
        self.cls.get_style_context().add_class("secondary-btn")
        self.cls.connect("clicked", lambda b: Gtk.main_quit())
        bot.pack_start(self.cls, False, False, 0)

        bot.pack_start(Gtk.Label(), True, True, 0)

        self.bck = Gtk.Button(label="Back")
        self.bck.get_style_context().add_class("secondary-btn")
        self.bck.connect("clicked", self.go_back)
        bot.pack_end(self.bck, False, False, 0)

        self.nxt = Gtk.Button(label="Next")
        self.nxt.get_style_context().add_class("primary-btn")
        self.nxt.set_margin_start(6)
        self.nxt.connect("clicked", self.go_next)
        bot.pack_end(self.nxt, False, False, 0)

        vb.pack_start(bot, False, False, 0)

        self.show_page(0)
        self.connect("destroy", Gtk.main_quit)
        self.show_all()

    def build_pages(self):
        self.build_welcome()
        self.build_appearance()
        self.build_desktop_setup()
        self.build_finish()

    def show_page(self, idx):
        if idx < 0 or idx >= self.total_pages:
            return
        direction = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT if idx > self.current_page else Gtk.StackTransitionType.OVER_RIGHT_LEFT
        self.stack.set_transition_type(direction)
        self.current_page = idx
        self.stack.set_visible_child_name(f"p{idx}")
        titles = ["Welcome", "Customize", "Settings", "Finish"]
        self.step_lbl.set_text(f"{titles[idx]}  ({idx + 1}/{self.total_pages})")
        self.update_nav()

    def update_nav(self):
        is_last = self.current_page == self.total_pages - 1
        self.bck.set_sensitive(self.current_page > 0)
        self.nxt.set_label("Apply & Finish" if is_last else "Next")
        self.nxt.set_sensitive(True)

    def go_back(self, btn):
        self.show_page(self.current_page - 1)

    def go_next(self, btn):
        if self.current_page == self.total_pages - 1:
            self.apply_final()
            open(FLAG_FILE, 'w').close()
            Gtk.main_quit()
        else:
            self.show_page(self.current_page + 1)

    def go_to_page(self, idx):
        self.show_page(idx)

    def build_welcome(self):
        p = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        p.set_margin_top(18)

        # Hero area
        hero = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        hero.set_halign(Gtk.Align.CENTER)
        hero.set_margin_bottom(10)

        logo_p = "/usr/share/icons/hicolor/scalable/apps/basalt-logo.svg"
        if os.path.exists(logo_p):
            try:
                pb = GdkPixbuf.Pixbuf.new_from_file_at_size(logo_p, 64, 64)
                hero.pack_start(Gtk.Image.new_from_pixbuf(pb), False, False, 0)
            except Exception:
                pass

        hero.pack_start(Gtk.Label(), False, False, 6)
        t = Gtk.Label()
        t.set_markup("<span size='xx-large' weight='bold'>Basalt Linux</span>")
        hero.pack_start(t, False, False, 0)

        t2 = Gtk.Label()
        t2.set_markup(f"<span size='large' foreground='#05b49e' weight='bold'>v{VERSION}</span>")
        hero.pack_start(t2, False, False, 0)

        hero.pack_start(Gtk.Label(), False, False, 4)
        t3 = Gtk.Label()
        t3.set_markup("<span size='small' foreground='#8899aa'>Built on Debian Trixie — Fast, Minimal, Beginner-Friendly</span>")
        hero.pack_start(t3, False, False, 0)

        p.pack_start(hero, False, False, 0)

        # Separator
        sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
        sep.set_margin_top(14)
        sep.set_margin_bottom(8)
        sep.set_margin_start(60)
        sep.set_margin_end(60)
        p.pack_start(sep, False, False, 0)

        # Hub buttons
        btns_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        btns_box.set_margin_start(40)
        btns_box.set_margin_end(40)

        btns = [
            ("Customize Appearance", "Theme, icons, cursor, and wallpaper", "preferences-desktop-theme-symbolic", 1),
            ("Desktop &amp; Setup", "Panel position, desktop icons, services", "preferences-system-symbolic", 2),
        ]
        for label, desc, icon, idx in btns:
            btns_box.pack_start(HubButton(label, desc, icon, idx, self.go_to_page), False, False, 0)

        p.pack_start(btns_box, True, True, 0)

        self.stack.add_titled(p, "p0", "Welcome")

    def build_appearance(self):
        p = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        p.set_margin_top(6)

        # Theme
        l = Gtk.Label()
        l.set_markup("<span size='small' foreground='#667'>GTK THEME</span>")
        l.set_xalign(0.0)
        l.set_margin_bottom(2)
        p.pack_start(l, False, False, 0)
        self.cb_theme = Gtk.ComboBoxText()
        for t in THEMES:
            self.cb_theme.append_text(t)
        self.cb_theme.set_active(0)
        self.cb_theme.connect("changed", self.on_theme)
        p.pack_start(self.cb_theme, False, False, 0)
        p.pack_start(Gtk.Label(), False, False, 6)

        # Icons
        l = Gtk.Label()
        l.set_markup("<span size='small' foreground='#667'>ICON THEME</span>")
        l.set_xalign(0.0)
        l.set_margin_bottom(2)
        p.pack_start(l, False, False, 0)
        self.cb_icon = Gtk.ComboBoxText()
        for i in ICONS:
            self.cb_icon.append_text(i)
        self.cb_icon.set_active(0)
        self.cb_icon.connect("changed", self.on_icon)
        p.pack_start(self.cb_icon, False, False, 0)
        p.pack_start(Gtk.Label(), False, False, 6)

        # Cursor
        l = Gtk.Label()
        l.set_markup("<span size='small' foreground='#667'>CURSOR THEME</span>")
        l.set_xalign(0.0)
        l.set_margin_bottom(2)
        p.pack_start(l, False, False, 0)
        self.cb_cursor = Gtk.ComboBoxText()
        for c in CURSORS:
            self.cb_cursor.append_text(c)
        self.cb_cursor.set_active(0)
        self.cb_cursor.connect("changed", self.on_cursor)
        p.pack_start(self.cb_cursor, False, False, 0)
        p.pack_start(Gtk.Label(), False, False, 8)

        # Wallpaper
        l = Gtk.Label()
        l.set_markup("<span size='small' foreground='#667'>WALLPAPER</span>")
        l.set_xalign(0.0)
        l.set_margin_bottom(4)
        p.pack_start(l, False, False, 0)

        flow = Gtk.FlowBox()
        flow.set_max_children_per_line(4)
        flow.set_selection_mode(Gtk.SelectionMode.SINGLE)
        flow.set_homogeneous(True)
        flow.set_column_spacing(6)
        flow.set_row_spacing(6)
        flow.connect("child-activated", self.on_wp)

        for fpath, name in WALLPAPERS:
            card = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
            card.set_size_request(100, 80)
            try:
                pb = GdkPixbuf.Pixbuf.new_from_file_at_size(fpath, 92, 50)
                card.pack_start(Gtk.Image.new_from_pixbuf(pb), False, False, 0)
            except Exception:
                pass
            lbl = Gtk.Label()
            lbl.set_text(name)
            lbl.get_style_context().add_class("subtitle-text")
            card.pack_start(lbl, False, False, 0)
            flow.add(card)

        scr = Gtk.ScrolledWindow()
        scr.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        scr.set_min_content_height(130)
        scr.add(flow)
        p.pack_start(scr, True, True, 0)

        self.stack.add_titled(p, "p1", "Appearance")

    def detect_panel_position(self):
        p = self.active_panel()
        r = subprocess.run(["xfconf-query", "-c", "xfce4-panel", "-p",
                           f"/panels/{p}/position"],
                          capture_output=True, text=True)
        if r.returncode == 0 and "p=6" in r.stdout.strip():
            return 0  # Top
        return 1  # Bottom

    def detect_desktop_icons(self):
        r = subprocess.run(["xfconf-query", "-c", "xfce4-desktop",
                           "-p", "/desktop-icons/icon-show"],
                          capture_output=True, text=True)
        return r.returncode == 0 and r.stdout.strip() == "true"

    def detect_service(self, name):
        r = subprocess.run(["systemctl", "is-enabled", name],
                          capture_output=True, text=True)
        return r.returncode == 0 and r.stdout.strip() == "enabled"

    def build_desktop_setup(self):
        p = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        p.set_margin_top(6)

        # Panel
        l = Gtk.Label()
        l.set_markup("<span size='small' foreground='#667'>PANEL POSITION</span>")
        l.set_xalign(0.0)
        l.set_margin_bottom(2)
        p.pack_start(l, False, False, 0)
        self.cb_panel = Gtk.ComboBoxText()
        self.cb_panel.append_text("Top")
        self.cb_panel.append_text("Bottom")
        self.cb_panel.set_active(self.detect_panel_position())
        self.cb_panel.connect("changed", self.on_panel)
        p.pack_start(self.cb_panel, False, False, 0)
        p.pack_start(Gtk.Label(), False, False, 10)

        # Desktop icons
        l = Gtk.Label()
        l.set_markup("<span size='small' foreground='#667'>DESKTOP ICONS</span>")
        l.set_xalign(0.0)
        l.set_margin_bottom(4)
        p.pack_start(l, False, False, 0)
        hb = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
        self.sw_icons = Gtk.Switch()
        self.sw_icons.set_active(self.detect_desktop_icons())
        self.sw_icons.connect("notify::active", self.on_icons)
        hb.pack_start(self.sw_icons, False, False, 0)
        hb.pack_start(Gtk.Label(label="Show desktop icons"), False, False, 0)
        p.pack_start(hb, False, False, 0)
        p.pack_start(Gtk.Label(), False, False, 14)

        # Services
        l = Gtk.Label()
        l.set_markup("<span size='small' foreground='#667'>SERVICES</span>")
        l.set_xalign(0.0)
        l.set_margin_bottom(4)
        p.pack_start(l, False, False, 0)

        items = [
            ("timeshift", "Timeshift Snapshots", "Automatic system snapshots for recovery"),
            ("flatpak", "Flatpak Support", "Access thousands of apps from Flathub"),
        ]
        for key, title, desc in items:
            b = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
            b.set_margin_bottom(8)
            vb = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=1)
            tl = Gtk.Label()
            tl.set_markup("<b>" + title + "</b>")
            tl.set_xalign(0.0)
            vb.pack_start(tl, False, False, 0)
            dl = Gtk.Label()
            dl.set_text(desc)
            dl.set_xalign(0.0)
            dl.get_style_context().add_class("feature-desc")
            vb.pack_start(dl, False, False, 0)
            b.pack_start(vb, True, True, 0)
            sw = Gtk.Switch()
            sw.set_active(self.detect_service(key))
            b.pack_start(sw, False, False, 0)
            self.sw_setup[key] = sw
            p.pack_start(b, False, False, 0)

        self.stack.add_titled(p, "p2", "Desktop & Setup")

    def build_finish(self):
        p = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        p.set_valign(Gtk.Align.CENTER)
        p.set_halign(Gtk.Align.CENTER)
        b = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        logo_p = "/usr/share/icons/hicolor/scalable/apps/basalt-logo.svg"
        if os.path.exists(logo_p):
            try:
                pb = GdkPixbuf.Pixbuf.new_from_file_at_size(logo_p, 56, 56)
                b.pack_start(Gtk.Image.new_from_pixbuf(pb), False, False, 10)
            except Exception:
                pass
        h = Gtk.Label()
        h.set_markup(f"<span size='x-large' weight='bold' foreground='{AC}'>All Set!</span>")
        b.pack_start(h, False, False, 5)
        d = Gtk.Label()
        d.set_markup("<span size='medium'>Your Basalt Linux is ready.\nEnjoy your fast, minimal, and secure desktop.</span>")
        d.set_justify(Gtk.Justification.CENTER)
        b.pack_start(d, False, False, 10)
        n = Gtk.Label()
        n.set_markup(f"<span size='small' foreground='#8899aa'>Theme, icons, and desktop changes are live.\nClick Finish to enable selected services.</span>")
        n.set_justify(Gtk.Justification.CENTER)
        b.pack_start(n, False, False, 5)
        p.pack_start(b, True, True, 0)
        self.stack.add_titled(p, "p3", "Finish")

    def on_theme(self, cb):
        t = cb.get_active_text()
        if t:
            GSETTINGS.set_property("gtk-theme-name", t)
            xset("xsettings", "/Net/ThemeName", t)
            xset("xsettings", "/Gtk/ThemeName", t)
            xset("xfwm4", "/general/theme", t)

    def on_icon(self, cb):
        t = cb.get_active_text()
        if t:
            GSETTINGS.set_property("gtk-icon-theme-name", t)
            xset("xsettings", "/Net/IconThemeName", t)
            xset("xsettings", "/Gtk/IconThemeName", t)

    def on_cursor(self, cb):
        t = cb.get_active_text()
        if t:
            GSETTINGS.set_property("gtk-cursor-theme-name", t)
            xset("xsettings", "/Gtk/CursorThemeName", t)

    def on_wp(self, flow, child):
        idx = child.get_index()
        if idx < len(WALLPAPERS):
            set_wallpaper(WALLPAPERS[idx][0])

    def active_panel(self):
        for p in ["panel-1", "panel-0"]:
            r = subprocess.run(["xfconf-query", "-c", "xfce4-panel",
                               "-p", f"/panels/{p}/plugin-ids"],
                              capture_output=True, text=True)
            if r.returncode == 0:
                return p
        return "panel-0"

    def on_panel(self, cb):
        p = self.active_panel()
        pos = cb.get_active_text()
        val = "p=6;x=0;y=0" if pos == "Top" else "p=4;x=0;y=0"
        xset("xfce4-panel", f"/panels/{p}/position", val)
        xset("xfce4-panel", f"/panels/{p}/position-locked", "true", "bool")
        subprocess.Popen(["xfce4-panel", "-r"])

    def on_icons(self, sw, gp):
        v = "true" if sw.get_active() else "false"
        xset("xfce4-desktop", "/desktop-icons/icon-show", v, "bool")
        xset("xfce4-desktop", "/desktop-icons/file-icons/show-filesystem", v, "bool")
        subprocess.Popen(["xfdesktop", "--reload"])

    def apply_final(self):
        if self.sw_setup.get("timeshift") and self.sw_setup["timeshift"].get_active() and \
           subprocess.run(["which", "timeshift"], capture_output=True).returncode == 0:
            subprocess.run(["pkexec", "systemctl", "enable", "--now", "timeshift"], capture_output=True)
            subprocess.run(["pkexec", "timeshift", "--create", "--comments", "Initial setup"], capture_output=True)
        if self.sw_setup.get("flatpak") and self.sw_setup["flatpak"].get_active() and \
           subprocess.run(["which", "flatpak"], capture_output=True).returncode == 0:
            subprocess.run(["pkexec", "systemctl", "enable", "--now", "flatpak"], capture_output=True)


def main():
    if os.path.exists(FLAG_FILE):
        sys.exit(0)
    if not os.environ.get("DISPLAY", ""):
        sys.exit(0)
    win = BasaltWelcome()
    Gtk.main()


if __name__ == "__main__":
    main()
