#!/usr/bin/env python3
"""42potential desktop launcher.

A native app, not a website wrapper: sign in (or create an account),
then see your software as boxes and your organizations (if you're in
any) -- same account as the website, cached locally so you don't have
to sign in every launch. Selecting a piece of software opens its own
focused window -- each tool is its own small program under
desktop/tools/<id>/, launched as a child process with your session
handed to it via the FORTYTWO_SESSION environment variable, so nothing
ever asks you to sign in twice. Organizations open on the website,
where the real management UI (create/join, members, join requests,
delete) already lives -- this app only shows which ones you're in.

Run:
    pip install -r requirements.txt
    python app.py
"""

import os
import subprocess
import sys
import tkinter as tk
import webbrowser
from tkinter import ttk

import auth

SITE = "https://42potential.com"
HERE = os.path.dirname(os.path.abspath(__file__))

ACCENT = "#e2562c"
INK = "#1b1711"
INK_DIM = "#5b5346"
BG = "#f6f3ec"
BG_RAISED = "#ffffff"

TOOLS = [
    {
        "id": "inventory",
        "name": "Inventory",
        "tagline": "Track every cabinet, tagged and searchable.",
        "unlock_path": "/api/inventory/unlock",
        "script": os.path.join(HERE, "tools", "inventory", "app.py"),
    },
    {
        "id": "helpdesk",
        "name": "Help Desk",
        "tagline": "Free remote support, with your consent every time.",
        "unlock_path": "/api/helpdesk/unlock",
        "script": os.path.join(HERE, "tools", "helpdesk", "agent.py"),
    },
]


def launch_tool(script_path, session_token):
    env = {**os.environ, "FORTYTWO_SESSION": session_token, "FORTYTWO_API": SITE}
    subprocess.Popen([sys.executable, script_path], env=env)


class LoginView(tk.Frame):
    def __init__(self, master, on_success):
        super().__init__(master, bg=BG)
        self.on_success = on_success

        tk.Label(self, text="42potential", bg=BG, fg=INK, font=("Segoe UI", 20, "bold")).pack(pady=(40, 4))
        tk.Label(self, text="sign in to see your software", bg=BG, fg=INK_DIM).pack(pady=(0, 20))

        tabs = tk.Frame(self, bg=BG)
        tabs.pack()
        tk.Button(tabs, text="Sign in", command=lambda: self._switch("login")).pack(side="left", padx=4)
        tk.Button(tabs, text="Create account", command=lambda: self._switch("signup")).pack(side="left", padx=4)

        self.form_frame = tk.Frame(self, bg=BG)
        self.form_frame.pack(pady=20)

        self.status_var = tk.StringVar()
        tk.Label(self, textvariable=self.status_var, bg=BG, fg=ACCENT, wraplength=360).pack()

        self._switch("login")

    def _switch(self, mode):
        self.status_var.set("")
        for w in self.form_frame.winfo_children():
            w.destroy()
        if mode == "login":
            self._build_login_form()
        else:
            self._build_signup_form()

    def _field(self, label, show=None):
        row = tk.Frame(self.form_frame, bg=BG)
        row.pack(fill="x", pady=4)
        tk.Label(row, text=label, bg=BG, fg=INK, width=10, anchor="w").pack(side="left")
        entry = tk.Entry(row, width=28, show=show)
        entry.pack(side="left")
        return entry

    def _build_login_form(self):
        self.login_email = self._field("email")
        self.login_password = self._field("password", show="*")
        tk.Button(self.form_frame, text="Sign in", bg=ACCENT, fg="white", command=self._do_login).pack(pady=10)

    def _build_signup_form(self):
        self.signup_name = self._field("name")
        self.signup_email = self._field("email")
        self.signup_phone = self._field("phone")
        self.signup_password = self._field("password", show="*")
        tk.Button(self.form_frame, text="Create account", bg=ACCENT, fg="white", command=self._do_signup).pack(pady=10)

    def _do_login(self):
        self.status_var.set("Signing in…")
        self.update_idletasks()
        try:
            token = auth.login(self.login_email.get(), self.login_password.get())
        except auth.AuthError as e:
            self.status_var.set(str(e))
            return
        auth.save_token_encrypted(token)
        self.on_success(token)

    def _do_signup(self):
        self.status_var.set("Creating account…")
        self.update_idletasks()
        try:
            token = auth.signup(
                self.signup_name.get(), self.signup_email.get(),
                self.signup_phone.get(), self.signup_password.get(),
            )
        except auth.AuthError as e:
            self.status_var.set(str(e))
            return
        auth.save_token_encrypted(token)
        self.on_success(token)


class MainShell(tk.Frame):
    def __init__(self, master, session_token, on_sign_out):
        super().__init__(master, bg=BG)
        self.session_token = session_token
        self.on_sign_out = on_sign_out

        top = tk.Frame(self, bg=BG)
        top.pack(fill="x", padx=16, pady=(12, 0))
        tk.Label(top, text="42potential", bg=BG, fg=INK, font=("Segoe UI", 14, "bold")).pack(side="left")
        tk.Button(top, text="Sign out", command=self._sign_out).pack(side="right")

        self.notebook = ttk.Notebook(self)
        self.notebook.pack(fill="both", expand=True, padx=16, pady=16)

        self.software_tab = tk.Frame(self.notebook, bg=BG)
        self.notebook.add(self.software_tab, text="Software")

        self.orgs_tab = tk.Frame(self.notebook, bg=BG)
        # only added to the notebook if you're actually in an org -- see _load_orgs

        self._load_software()
        self._load_orgs()

    def _sign_out(self):
        auth.clear_token()
        self.on_sign_out()

    def _load_software(self):
        for w in self.software_tab.winfo_children():
            w.destroy()
        any_owned = False
        for tool in TOOLS:
            ok, data = auth.api(tool["unlock_path"], self.session_token)
            if ok and data.get("unlocked"):
                any_owned = True
                self._software_box(tool)
        if not any_owned:
            tk.Label(
                self.software_tab, bg=BG, fg=INK_DIM, wraplength=360,
                text="Nothing on your account yet -- add some from the catalog on the website.",
            ).pack(pady=20)

    def _software_box(self, tool):
        box = tk.Frame(self.software_tab, bg=BG_RAISED, highlightbackground=INK, highlightthickness=2)
        box.pack(fill="x", pady=6, ipady=8, ipadx=10)
        tk.Label(box, text=tool["name"], bg=BG_RAISED, fg=INK, font=("Segoe UI", 12, "bold")).pack(anchor="w", padx=12, pady=(8, 0))
        tk.Label(box, text=tool["tagline"], bg=BG_RAISED, fg=INK_DIM, wraplength=320, justify="left").pack(anchor="w", padx=12)
        tk.Button(box, text="Open", bg=ACCENT, fg="white",
                  command=lambda t=tool: launch_tool(t["script"], self.session_token)).pack(anchor="e", padx=12, pady=8)

    def _load_orgs(self):
        ok, data = auth.api("/api/org/mine", self.session_token)
        if not ok:
            return
        owned = data.get("owned") or []
        member_of = data.get("memberOf") or []
        if not owned and not member_of:
            return

        if str(self.orgs_tab) not in self.notebook.tabs():
            self.notebook.add(self.orgs_tab, text="Organizations")
        for w in self.orgs_tab.winfo_children():
            w.destroy()
        for org in owned:
            status = org.get("status")
            role = "owner" if status == "approved" else f"owner ({status})"
            self._org_box(org, role)
        for org in member_of:
            self._org_box(org, "member")

    def _org_box(self, org, role):
        box = tk.Frame(self.orgs_tab, bg=BG_RAISED, highlightbackground=INK, highlightthickness=2)
        box.pack(fill="x", pady=6, ipady=8, ipadx=10)
        tk.Label(box, text=org.get("name", ""), bg=BG_RAISED, fg=INK, font=("Segoe UI", 12, "bold")).pack(anchor="w", padx=12, pady=(8, 0))
        tk.Label(box, text=role, bg=BG_RAISED, fg=INK_DIM).pack(anchor="w", padx=12)
        tk.Button(box, text="Manage on website",
                  command=lambda: webbrowser.open(f"{SITE}/organizations.html")).pack(anchor="e", padx=12, pady=8)


class App:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("42potential")
        self.root.configure(bg=BG)
        self.root.geometry("460x640")
        self.root.minsize(380, 480)
        self.root.bind("<F11>", self._toggle_fullscreen)

        self.container = tk.Frame(self.root, bg=BG)
        self.container.pack(fill="both", expand=True)

        token = auth.load_token_encrypted()
        if token:
            self._show_shell(token)
        else:
            self._show_login()

    def _toggle_fullscreen(self, _event):
        self.root.attributes("-fullscreen", not self.root.attributes("-fullscreen"))

    def _clear(self):
        for w in self.container.winfo_children():
            w.destroy()

    def _show_login(self):
        self._clear()
        LoginView(self.container, on_success=self._show_shell).pack(fill="both", expand=True)

    def _show_shell(self, token):
        # the cached token (from a previous launch) may have expired --
        # sessions last 7 days -- so confirm it's still good before
        # trusting it, otherwise this would silently show an empty
        # Software tab with no explanation instead of asking to sign in
        ok, data = auth.api("/api/me", token)
        if not ok or not data.get("loggedIn"):
            auth.clear_token()
            self._show_login()
            return
        self._clear()
        MainShell(self.container, token, on_sign_out=self._show_login).pack(fill="both", expand=True)

    def run(self):
        self.root.mainloop()


if __name__ == "__main__":
    App().run()
