package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"gopkg.in/yaml.v3"
)

// --- Config types ---

type ServerConfig struct {
	Port int    `yaml:"port"`
	Host string `yaml:"host"`
}

type AppConfig struct {
	Name        string `yaml:"name"`
	Description string `yaml:"description"`
}

type Config struct {
	Server ServerConfig `yaml:"server"`
	App    AppConfig    `yaml:"app"`
}

type AppEntry struct {
	Name     string   `yaml:"name"`
	Subtitle string   `yaml:"subtitle"`
	Path     string   `yaml:"path"`
	Upstream string   `yaml:"upstream"`
	Icon     string   `yaml:"icon"`
	Groups   []string `yaml:"groups"`
}

type AppsFile struct {
	Apps []AppEntry `yaml:"apps"`
}

type Settings struct {
	Company struct {
		Name     string `yaml:"name"`
		Subtitle string `yaml:"subtitle"`
		Logo     string `yaml:"logo"`
		TopUser  string `yaml:"top_user"`
		Language string `yaml:"language"`
		Timezone string `yaml:"timezone"`
	} `yaml:"company"`
	SMTP struct {
		Host   string `yaml:"host"`
		Port   int    `yaml:"port"`
		User   string `yaml:"user"`
		Sender string `yaml:"sender"`
	} `yaml:"smtp"`
	IMAP struct {
		Host string `yaml:"host"`
		Port int    `yaml:"port"`
	} `yaml:"imap"`
}

// --- User settings ---

type UserSettings struct {
	FirstName      string `json:"first_name,omitempty"`
	LastName       string `json:"last_name,omitempty"`
	ProfilePicture string `json:"profile_picture,omitempty"`
	Email          string `json:"email,omitempty"`
	EmailPassword  string `json:"email_password,omitempty"`
	Language       string `json:"language,omitempty"`
	Timezone       string `json:"timezone,omitempty"`
	Phone          string `json:"phone,omitempty"`
	Address        string `json:"address,omitempty"`
	Messenger      string `json:"messenger,omitempty"`
	Bio            string `json:"bio,omitempty"`
	Manager        string `json:"manager,omitempty"`
	JobTitle       string `json:"job_title,omitempty"`
	Dept           string `json:"dept,omitempty"`
}

func loadUserSettings(username string) *UserSettings {
	path := filepath.Join("/opt/nextworkspace/data/users", username+".json")
	data, err := os.ReadFile(path)
	if err != nil {
		return &UserSettings{}
	}
	var s UserSettings
	json.Unmarshal(data, &s)
	return &s
}

func saveUserSettings(username string, s *UserSettings) error {
	path := filepath.Join("/opt/nextworkspace/data/users", username+".json")
	os.MkdirAll(filepath.Dir(path), 0700)
	data, _ := json.MarshalIndent(s, "", "  ")
	return os.WriteFile(path, data, 0600)
}

// --- Config loading ---

func loadConfig(configDir string) (*Config, error) {
	path := filepath.Join(configDir, "config.yaml")
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("reading config: %w", err)
	}
	var cfg Config
	if err := yaml.Unmarshal(data, &cfg); err != nil {
		return nil, fmt.Errorf("parsing config: %w", err)
	}
	return &cfg, nil
}

func loadApps(configDir string) ([]AppEntry, error) {
	path := filepath.Join(configDir, "apps.yaml")
	data, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return []AppEntry{}, nil
		}
		return nil, fmt.Errorf("reading apps: %w", err)
	}
	var appsFile AppsFile
	if err := yaml.Unmarshal(data, &appsFile); err != nil {
		return nil, fmt.Errorf("parsing apps: %w", err)
	}
	return appsFile.Apps, nil
}

// --- Settings loading ---

func loadSettings(configDir string) (*Settings, error) {
	path := filepath.Join(configDir, "settings.yaml")
	data, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return &Settings{}, nil
		}
		return nil, fmt.Errorf("reading settings: %w", err)
	}
	var s Settings
	if err := yaml.Unmarshal(data, &s); err != nil {
		return nil, fmt.Errorf("parsing settings: %w", err)
	}
	return &s, nil
}

func saveSettings(configDir string, s *Settings) error {
	path := filepath.Join(configDir, "settings.yaml")
	data, err := yaml.Marshal(s)
	if err != nil {
		return fmt.Errorf("marshaling settings: %w", err)
	}
	return os.WriteFile(path, data, 0644)
}

func updateEnvFile(path string, values map[string]string) error {
	data, err := os.ReadFile(path)
	if err != nil {
		return err
	}
	lines := strings.Split(string(data), "\n")
	for k, v := range values {
		found := false
		for i, line := range lines {
			if strings.HasPrefix(line, k+"=") {
				lines[i] = k + "=" + v
				found = true
				break
			}
		}
		if !found {
			lines = append(lines, k+"="+v)
		}
	}
	return os.WriteFile(path, []byte(strings.Join(lines, "\n")), 0600)
}

// --- Helpers ---

func userHasAnyGroup(userGroups []string, requiredGroups []string) bool {
	for _, ug := range userGroups {
		for _, rg := range requiredGroups {
			if strings.TrimSpace(ug) == rg {
				return true
			}
		}
	}
	return false
}

// --- Auth middleware (trusts Remote-User from Caddy forward auth) ---

func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := r.Header.Get("Remote-User")
		if user == "" {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}
		r.Header.Set("X-Auth-User", user)
		next(w, r)
	}
}

func adminGroupMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		groups := r.Header.Get("Remote-Groups")
		if !strings.Contains(groups, "admins") {
			http.Error(w, "Forbidden — admins only", http.StatusForbidden)
			return
		}
		next(w, r)
	}
}

// --- Handlers ---

func healthHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, "OK")
}

func launcherHandler(cfg *Config, apps []AppEntry) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := r.Header.Get("Remote-User")
		groupsHeader := r.Header.Get("Remote-Groups")
		userGroups := strings.Split(groupsHeader, ",")
		settings, _ := loadSettings(os.Getenv("CONFIG_DIR"))
		us := loadUserSettings(user)
		lang := us.Language
		if lang == "" && settings != nil {
			lang = settings.Company.Language
		}
		if lang == "" {
			lang = "en"
		}
		// Use display name if available
		displayName := user
		if us.FirstName != "" {
			displayName = us.FirstName
			if us.LastName != "" {
				displayName += " " + us.LastName
			}
		}

		var allowedApps []AppEntry
		for _, app := range apps {
			if len(app.Groups) == 0 || userHasAnyGroup(userGroups, app.Groups) {
				allowedApps = append(allowedApps, app)
			}
		}

		data := struct {
			AppName     string
			Description string
			User        string
			DisplayName string
			IsAdmin     bool
			Apps        []AppEntry
			CompanyLogo string
			Subtitle    string
			Lang        string
		}{
			AppName:     cfg.App.Name,
			Description: cfg.App.Description,
			User:        user,
			DisplayName: displayName,
			IsAdmin:     strings.Contains(groupsHeader, "admins"),
			Apps:        allowedApps,
			CompanyLogo: settings.Company.Logo,
			Subtitle:    settings.Company.Subtitle,
			Lang:        lang,
		}
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		launcherTmpl.Execute(w, data)
	}
}

func proxyToUpstream(upstream string) http.HandlerFunc {
	target, err := url.Parse(upstream)
	if err != nil {
		log.Fatalf("Invalid upstream URL %q: %v", upstream, err)
	}
	proxy := httputil.NewSingleHostReverseProxy(target)
	return func(w http.ResponseWriter, r *http.Request) {
		r.Header.Set("Remote-User", r.Header.Get("Remote-User"))
		proxy.ServeHTTP(w, r)
	}
}

func adminHandler(w http.ResponseWriter, r *http.Request) {
	apiBase := "http://authelia:8080"
	apiToken := os.Getenv("AUTHELIA_SECRET")
	configDir := os.Getenv("CONFIG_DIR")
	if configDir == "" {
		configDir = "/opt/nextworkspace/config/nextworkspace"
	}

	// Load settings for Global tab
	settings, _ := loadSettings(configDir)
	domain := os.Getenv("DOMAIN")
	adminEmail := os.Getenv("TLS_EMAIL")

	switch r.Method {
	case http.MethodGet:
		// If settings JSON format requested
		if r.URL.Query().Get("format") == "settings" {
			w.Header().Set("Content-Type", "application/json")
			jsonData := fmt.Sprintf(`{"company_name":"%s","language":"%s","timezone":"%s","smtp_host":"%s","smtp_port":"%d","smtp_user":"%s","smtp_sender":"%s","imap_host":"%s","imap_port":"%d"}`,
				settings.Company.Name, settings.Company.Language, settings.Company.Timezone,
				settings.SMTP.Host, settings.SMTP.Port, settings.SMTP.User, settings.SMTP.Sender,
				settings.IMAP.Host, settings.IMAP.Port)
			w.Write([]byte(jsonData))
			return
		}

		// If JSON format requested, return raw API response
		if r.URL.Query().Get("format") == "json" {
			req, _ := http.NewRequest("GET", apiBase+"/api/users", nil)
			req.Header.Set("Authorization", "Bearer "+apiToken)
			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusBadGateway)
				return
			}
			defer resp.Body.Close()
			body, _ := io.ReadAll(resp.Body)
			w.Header().Set("Content-Type", "application/json")
			w.Write(body)
			return
		}

		w.Header().Set("Content-Type", "text/html; charset=utf-8")

		// Check service statuses
		autheliaUp := false
		if resp, err := http.Get("http://authelia:9091/api/health"); err == nil {
			autheliaUp = resp.StatusCode == 200
			resp.Body.Close()
		}
		caddyUp := false
		// Check port 80 responds (don't follow HTTPS redirect)
		if conn, err := net.DialTimeout("tcp", "caddy:80", 3*time.Second); err == nil {
			caddyUp = true
			conn.Close()
		}
		launcherUp := false
		if resp, err := http.Get("http://127.0.0.1:9000/health"); err == nil {
			launcherUp = resp.StatusCode == 200
			resp.Body.Close()
		}

		userLang := os.Getenv("LANG")
		if userLang == "" {
			userLang = "en"
		}
		if settings != nil && settings.Company.Language != "" {
			userLang = settings.Company.Language
		}

		// Fetch all users for TopUser dropdown
		allUsers, _ := fetchAllUsers()
		type UserOption struct {
			Username    string
			DisplayName string
		}
		var allUserOptions []UserOption
		for _, u := range allUsers {
			profile := loadUserSettings(u.Username)
			name := u.DisplayName
			if profile.FirstName != "" {
				name = profile.FirstName + " " + profile.LastName
			}
			allUserOptions = append(allUserOptions, UserOption{Username: u.Username, DisplayName: name})
		}

		adminTmpl.Execute(w, map[string]interface{}{
			"Settings":   settings,
			"Domain":     domain,
			"AdminEmail": adminEmail,
			"AllUsers":   allUserOptions,
			"AutheliaUp": autheliaUp,
			"CaddyUp":    caddyUp,
			"LauncherUp": launcherUp,
			"Lang":       userLang,
			"Version":    appVersion,
		})

	case http.MethodPost:
		// Create user
		body, _ := io.ReadAll(r.Body)
		req, _ := http.NewRequest("POST", apiBase+"/api/users/bulk", bytes.NewReader(body))
		req.Header.Set("Authorization", "Bearer "+apiToken)
		req.Header.Set("Content-Type", "application/json")
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusBadGateway)
			return
		}
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)

	case http.MethodDelete:
		username := r.URL.Query().Get("username")
		req, _ := http.NewRequest("DELETE", apiBase+"/api/users/"+username, nil)
		req.Header.Set("Authorization", "Bearer "+apiToken)
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			http.Error(w, fmt.Sprintf("API error: %v", err), http.StatusBadGateway)
			return
		}
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	}
}

func settingsHandler(w http.ResponseWriter, r *http.Request) {
	user := r.Header.Get("Remote-User")
	groups := r.Header.Get("Remote-Groups")
	isAdmin := strings.Contains(groups, "admins")
	us := loadUserSettings(user)
	settings, _ := loadSettings(os.Getenv("CONFIG_DIR"))
	lang := us.Language
	if lang == "" && settings != nil {
		lang = settings.Company.Language
	}
	if lang == "" {
		lang = "en"
	}

	// Fetch all users for manager dropdown
	allUsers, _ := fetchAllUsers()
	type UserOption struct {
		Username    string
		DisplayName string
	}
	var allUserOptions []UserOption
	for _, u := range allUsers {
		profile := loadUserSettings(u.Username)
		name := u.DisplayName
		if profile.FirstName != "" {
			name = profile.FirstName + " " + profile.LastName
		}
		allUserOptions = append(allUserOptions, UserOption{Username: u.Username, DisplayName: name})
	}

	tmpl := template.Must(template.New("settings").Funcs(funcMap).Parse(settingsHTML))
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	tmpl.Execute(w, map[string]interface{}{
		"User":         user,
		"IsAdmin":      isAdmin,
		"UserSettings": us,
		"AllUsers":     allUserOptions,
		"Settings":     settings,
		"Lang":         lang,
	})
}

func userSettingsSaveHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	user := r.Header.Get("Remote-User")
	if user == "" {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}
	us := &UserSettings{
		FirstName:      r.FormValue("first_name"),
		LastName:       r.FormValue("last_name"),
		ProfilePicture: r.FormValue("profile_picture"),
		Email:          r.FormValue("email"),
		EmailPassword:  r.FormValue("email_password"),
		Language:       r.FormValue("language"),
		Timezone:       r.FormValue("timezone"),
		Phone:          r.FormValue("phone"),
		Address:        r.FormValue("address"),
		Messenger:      r.FormValue("messenger"),
		Bio:            r.FormValue("bio"),
		Manager:        r.FormValue("manager"),
		JobTitle:       r.FormValue("job_title"),
		Dept:           r.FormValue("dept"),
	}
	if err := saveUserSettings(user, us); err != nil {
		http.Error(w, "Failed to save", http.StatusInternalServerError)
		return
	}
	w.Write([]byte(`{"status":"ok"}`))
}

// --- People directory ---

type apiUser struct {
	Username    string `json:"username"`
	DisplayName string `json:"display_name"`
}

func fetchAllUsers() ([]apiUser, error) {
	apiToken := os.Getenv("AUTHELIA_SECRET")
	req, _ := http.NewRequest("GET", "http://authelia:8080/api/users", nil)
	req.Header.Set("Authorization", "Bearer "+apiToken)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var users []apiUser
	json.NewDecoder(resp.Body).Decode(&users)
	return users, nil
}

func peopleDirectoryHandler(w http.ResponseWriter, r *http.Request) {
	// If path is /people/{username}, show profile
	username := strings.TrimPrefix(r.URL.Path, "/people/")
	if username != "" && username != "/" && !strings.Contains(username, "/") {
		peopleProfileHandler(w, r, username)
		return
	}

	users, err := fetchAllUsers()
	if err != nil {
		http.Error(w, "Failed to fetch users", http.StatusBadGateway)
		return
	}

	type Person struct {
		Username    string
		DisplayName string
		JobTitle    string
		Dept        string
		ProfilePic  string
	}
	var people []Person
	for _, u := range users {
		us := loadUserSettings(u.Username)
		displayName := u.DisplayName
		if us.FirstName != "" {
			displayName = us.FirstName + " " + us.LastName
		}
		people = append(people, Person{
			Username:    u.Username,
			DisplayName: displayName,
			JobTitle:    us.JobTitle,
			Dept:        us.Dept,
			ProfilePic:  us.ProfilePicture,
		})
	}

	tmpl := template.Must(template.New("people").Funcs(template.FuncMap{"t": t, "initial": initials}).Parse(peopleHTML))
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	tmpl.Execute(w, map[string]interface{}{"People": people, "Lang": "en"})
}

func peopleProfileHandler(w http.ResponseWriter, r *http.Request, username string) {
	us := loadUserSettings(username)

	var managerName string
	if us.Manager != "" {
		mgr := loadUserSettings(us.Manager)
		if mgr.FirstName != "" {
			managerName = mgr.FirstName + " " + mgr.LastName
		} else {
			managerName = us.Manager
		}
	}

	tmpl := template.Must(template.New("profile").Funcs(template.FuncMap{"t": t, "initial": initials}).Parse(profileHTML))
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	tmpl.Execute(w, map[string]interface{}{
		"Username":    username,
		"Profile":     us,
		"ManagerName": managerName,
		"Lang":        "en",
	})
}

func initials(s string) string {
	parts := strings.Fields(s)
	if len(parts) == 0 {
		return "?"
	}
	var result string
	for _, p := range parts {
		if len(p) > 0 {
			result += strings.ToUpper(string(p[0]))
		}
	}
	if len(result) > 2 {
		result = result[:2]
	}
	return result
}

// --- API proxy for authelia-api ---

func apiProxyHandler(w http.ResponseWriter, r *http.Request) {
	target, _ := url.Parse("http://authelia:8080")
	proxy := httputil.NewSingleHostReverseProxy(target)
	// Inject Bearer token for authelia-api auth
	token := os.Getenv("AUTHELIA_SECRET")
	if token != "" {
		r.Header.Set("Authorization", "Bearer "+token)
	}
	r.URL.Path = strings.TrimPrefix(r.URL.Path, "/api")
	proxy.ServeHTTP(w, r)
}

// --- Global settings handlers ---

func globalSettingsHandler(w http.ResponseWriter, r *http.Request) {
	configDir := os.Getenv("CONFIG_DIR")
	if configDir == "" {
		configDir = "/opt/nextworkspace/config/nextworkspace"
	}
	settings, err := loadSettings(configDir)
	if err != nil {
		settings = &Settings{}
	}

	domain := os.Getenv("DOMAIN")
	adminEmail := os.Getenv("TLS_EMAIL")
	smtpHost := os.Getenv("SMTP_HOST")
	smtpPort := os.Getenv("SMTP_PORT")

	// Check Authelia health
	autheliaUp := false
	if resp, err := http.Get("http://authelia:9091/api/health"); err == nil {
		autheliaUp = resp.StatusCode == 200
		resp.Body.Close()
	}

	tmpl := template.Must(template.New("global").Parse(globalSettingsHTML))
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	tmpl.Execute(w, map[string]interface{}{
		"CompanyName": settings.Company.Name,
		"Language":    settings.Company.Language,
		"Timezone":    settings.Company.Timezone,
		"SMTPHost":    smtpHost,
		"SMTPPort":    smtpPort,
		"SMTPUser":    settings.SMTP.User,
		"SMTPSender":  settings.SMTP.Sender,
		"Domain":      domain,
		"AdminEmail":  adminEmail,
		"AutheliaUp":  autheliaUp,
		"Saved":       r.URL.Query().Get("saved") == "ok",
	})
}

func globalSettingsSaveHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	configDir := os.Getenv("CONFIG_DIR")
	if configDir == "" {
		configDir = "/opt/nextworkspace/config/nextworkspace"
	}

	settings, _ := loadSettings(configDir)
	if settings == nil {
		settings = &Settings{}
	}

	settings.Company.Name = r.FormValue("company_name")
	settings.Company.Subtitle = r.FormValue("company_subtitle")
	settings.Company.Logo = r.FormValue("company_logo")
	settings.Company.TopUser = r.FormValue("top_user")
	settings.Company.Language = r.FormValue("language")
	settings.Company.Timezone = r.FormValue("timezone")
	settings.SMTP.Host = r.FormValue("smtp_host")
	settings.SMTP.Port, _ = strconv.Atoi(r.FormValue("smtp_port"))
	settings.SMTP.User = r.FormValue("smtp_user")
	settings.SMTP.Sender = r.FormValue("smtp_sender")
	settings.IMAP.Host = r.FormValue("imap_host")
	settings.IMAP.Port, _ = strconv.Atoi(r.FormValue("imap_port"))

	saveSettings(configDir, settings)

	// Update .env with SMTP values
	envPath := os.Getenv("ENV_FILE")
	if envPath == "" {
		envPath = "/opt/backup/.env"
	}
	envUpdates := map[string]string{
		"SMTP_HOST":   r.FormValue("smtp_host"),
		"SMTP_PORT":   r.FormValue("smtp_port"),
		"SMTP_USER":   r.FormValue("smtp_user"),
		"SMTP_SENDER": r.FormValue("smtp_sender"),
	}
	if pwd := r.FormValue("smtp_password"); pwd != "" {
		envUpdates["SMTP_PASS"] = pwd
	}
	updateEnvFile(envPath, envUpdates)

	http.Redirect(w, r, "/config?s=global&saved=ok", http.StatusFound)
}

func applySettingsHandler(w http.ResponseWriter, r *http.Request) {
	// Find nextwks.sh in common locations
	scriptPaths := []string{
		"/home/master/nextwks.sh",
		"/root/nextwks.sh",
		"/opt/backup/nextwks.sh",
	}
	script := ""
	for _, p := range scriptPaths {
		if _, err := os.Stat(p); err == nil {
			script = p
			break
		}
	}
	if script == "" {
		http.Error(w, `{"success":false,"error":"nextwks.sh not found"}`, http.StatusInternalServerError)
		return
	}

	cmd := exec.Command("bash", script, "--update")
	output, err := cmd.CombinedOutput()
	if err != nil {
		http.Error(w, fmt.Sprintf("Apply failed: %v\n%s", err, string(output)), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"success":true,"output":"%s"}`, strings.ReplaceAll(string(output), `"`, `\"`))
}

// --- Public settings API ---

func publicSettingsHandler(w http.ResponseWriter, r *http.Request) {
	configDir := os.Getenv("CONFIG_DIR")
	if configDir == "" {
		configDir = "/opt/nextworkspace/config/nextworkspace"
	}
	settings, _ := loadSettings(configDir)
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"name":"%s","subtitle":"%s","logo":"%s"}`,
		settings.Company.Name, settings.Company.Subtitle, settings.Company.Logo)
}

// --- Translation system ---

var translations = make(map[string]map[string]string)
var (
	launcherTmpl *template.Template
	funcMap      template.FuncMap
)
var adminTmpl *template.Template
var appVersion = "dev"

func loadTranslations(lngDir string) {
	langs := []string{"en", "de"}
	files := []string{"launcher.yaml", "admin.yaml"}

	for _, lang := range langs {
		if translations[lang] == nil {
			translations[lang] = make(map[string]string)
		}
		for _, file := range files {
			path := filepath.Join(lngDir, lang, file)
			data, err := os.ReadFile(path)
			if err != nil {
				continue
			}
			var keys map[string]string
			if err := yaml.Unmarshal(data, &keys); err != nil {
				continue
			}
			for k, v := range keys {
				translations[lang][k] = v
			}
		}
	}
}

func t(lang, key string, args ...string) string {
	text := translations[lang][key]
	if text == "" {
		text = translations["en"][key]
	}
	if text == "" {
		return key
	}
	for i := 0; i < len(args); i += 2 {
		text = strings.ReplaceAll(text, "{"+args[i]+"}", args[i+1])
	}
	return text
}

func getUserLang(r *http.Request) string {
	lang := r.Header.Get("Accept-Language")
	if strings.HasPrefix(lang, "de") {
		return "de"
	}
	settings, _ := loadSettings(os.Getenv("CONFIG_DIR"))
	if settings != nil && settings.Company.Language == "de" {
		return "de"
	}
	return "en"
}

// --- Templates ---

const landingPageHTML = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>NextWorkspace</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: #f0f2f5;
      color: #1a1a2e;
      min-height: 100vh;
    }
    header { background: linear-gradient(135deg, #1a1a2e, #16213e); color: #fff; padding: 3rem 2rem; text-align: center; }
    header h1 { font-size: 2.5rem; margin-bottom: 0.5rem; }
    header p { color: #a0aec0; font-size: 1.2rem; }
    .domain { color: #63b3ed; font-size: 0.9rem; margin-top: 0.5rem; }
    .container { max-width: 800px; margin: 0 auto; padding: 2rem; }
    h2 { font-size: 1.5rem; margin: 2rem 0 1rem; color: #2d3748; }
    ul { list-style: none; padding: 0; }
    li { background: #fff; border-radius: 8px; padding: 1rem 1.25rem; margin-bottom: 0.75rem; box-shadow: 0 1px 4px rgba(0,0,0,0.06); }
    li a { color: #1a1a2e; text-decoration: none; font-weight: 600; }
    li a:hover { color: #63b3ed; }
    li span { color: #718096; font-size: 0.9rem; margin-left: 0.5rem; }
    .ai-credit { background: #edf2f7; border-radius: 8px; padding: 1.5rem; margin-top: 2rem; text-align: center; font-size: 0.9rem; color: #4a5568; }
    footer { text-align: center; padding: 2rem; color: #a0aec0; font-size: 0.85rem; }
  </style>
</head>
<body>
  <header>
    <h1>NextWorkspace</h1>
    <p>Your Self-Hosted Workspace for Startups</p>
    <div class="domain">nextwks.eu</div>
  </header>
  <div class="container">
    <h2>Components</h2>
    <ul>
      <li><a href="https://app.nextwks.eu/home">NextWks Core</a><span>— Launcher &amp; Workspace Hub</span></li>
      <li><a href="https://app.nextwks.eu/drive">OpenCloud</a><span>— File Storage</span></li>
      <li><a href="https://app.nextwks.eu/connect">Alps Webmail</a><span>— Email Client</span></li>
      <li><a href="https://app.nextwks.eu/office">Euro Office</a><span>— Collaborative Suite</span></li>
      <li><a href="https://app.nextwks.eu/enterprise">ERPNext</a><span>— Enterprise ERP</span></li>
      <li><a href="https://app.nextwks.eu/chat">Element Web</a><span>— Matrix Chat</span></li>
      <li><a href="https://app.nextwks.eu/meet">Jitsi</a><span>— Video Conferencing</span></li>
      <li><a href="https://app.nextwks.eu/aida">Open WebUI</a><span>— AI Chat Frontend</span></li>
    </ul>
    <div class="ai-credit">
      <h2>Built with AI</h2>
      <p>NextWorkspace was developed with assistance from AI coding tools, using <strong>DeepSeek</strong> as the provider and <strong>OpenCode</strong> as the development framework.</p>
    </div>
  </div>
  <footer>&copy; 2026 NextWorkspace &mdash; nextwks.eu</footer>
</body>
</html>`

const launcherHTML = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{.AppName}}</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: #f0f2f5;
      color: #1a1a2e;
      min-height: 100vh;
    }
    header { background: linear-gradient(135deg, #1a1a2e, #16213e); color: #fff; padding: 2rem; text-align: center; }
    header .logo { max-height: 64px; margin-bottom: 0.5rem; }
    header h1 { font-size: 2rem; margin-bottom: 0.25rem; }
    header p { color: #a0aec0; font-size: 1rem; }
    .user-banner { background: #2d3748; color: #e2e8f0; padding: 0.75rem 2rem; text-align: center; font-size: 0.9rem; display: flex; justify-content: center; gap: 1rem; }
    .user-banner a { color: #63b3ed; text-decoration: none; }
    .user-banner a:hover { text-decoration: underline; }
    .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1.5rem; padding: 2rem; max-width: 1200px; margin: 0 auto; }
    .card { background: #fff; border-radius: 12px; padding: 1.5rem; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.08); transition: transform 0.2s; text-decoration: none; color: inherit; display: block; }
    .card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.12); }
    .icon { width: 48px; height: 48px; margin: 0 auto 1rem; background: #edf2f7; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; }
    .card h3 { font-size: 1.1rem; margin-bottom: 0.25rem; }
    .card p { color: #718096; font-size: 0.85rem; }
  </style>
</head>
<body>
  <header>
    {{if .CompanyLogo}}<img src="{{.CompanyLogo}}" class="logo" alt="Logo">{{end}}
    <h1>{{.AppName}}</h1>
    {{if .Subtitle}}<p>{{.Subtitle}}</p>{{end}}
  </header>
  <div class="user-banner">
    <span>{{t .Lang "welcome" "user" .DisplayName}}</span>
    <a href="/people">People</a>
    <a href="/settings">{{t .Lang "settings"}}</a>
    <a href="https://auth.nextwks.eu/logout">{{t .Lang "logout"}}</a>
  </div>
  <div class="grid">
    {{range .Apps}}
      {{if .Upstream}}
        <a class="card" href="{{.Path}}/" target="_blank" rel="noopener">
      {{else}}
        <div class="card" style="opacity:0.5; cursor:default;">
      {{end}}
        <div class="icon">{{.Icon}}</div>
        <h3>{{.Name}}</h3>
        <p>{{.Subtitle}}</p>
      {{if .Upstream}}</a>{{else}}</div>{{end}}
    {{end}}
  </div>
</body>
</html>`

const settingsHTML = `<!DOCTYPE html>
<html lang="{{.Lang}}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{t .Lang "settings"}} — NextWorkspace</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; color: #1a1a2e; }
    header { background: linear-gradient(135deg, #1a1a2e, #16213e); color: #fff; padding: 1rem 1.5rem; display: flex; justify-content: space-between; align-items: center; }
    header h1 { font-size: 1.2rem; }
    header a { color: #63b3ed; text-decoration: none; font-size: 0.85rem; }
    .container { max-width: 700px; margin: 2rem auto; padding: 0 1rem; }
    .card { background: #fff; border-radius: 10px; padding: 1.5rem; margin-bottom: 1.25rem; box-shadow: 0 1px 3px rgba(0,0,0,0.05); border: 1px solid #edf2f7; }
    .card h2 { font-size: 1rem; font-weight: 600; color: #2d3748; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid #edf2f7; }
    .field { margin-bottom: 0.9rem; }
    .field label { display: block; font-size: 0.82rem; font-weight: 500; color: #4a5568; margin-bottom: 0.25rem; }
    .field input, .field select { width: 100%; padding: 0.5rem 0.7rem; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 0.88rem; outline: none; }
    .field input:focus, .field select:focus { border-color: #63b3ed; box-shadow: 0 0 0 2px rgba(99,179,237,0.15); }
    .field-note { font-size: 0.8rem; color: #a0aec0; margin-bottom: 0.5rem; }
    .field-row { display: flex; gap: 1rem; }
    .field-row .field { flex: 1; }
    .btn { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.88rem; font-weight: 500; cursor: pointer; border: none; }
    .btn-primary { background: #1a1a2e; color: #fff; }
    .btn-primary:hover { background: #2d3748; }
    .actions { display: flex; gap: 0.75rem; align-items: center; margin-top: 1rem; }
    .saved-msg { color: #48bb78; font-size: 0.9rem; display: none; }
  </style>
</head>
<body>
  <header>
    <h1>{{t .Lang "settings"}}</h1>
    <a href="/home/">{{t .Lang "launcher_title"}}</a>
  </header>
  <div class="container">
    <form id="settings-form" onsubmit="return saveSettings(event)">
      <div class="card">
        <h2>Profile</h2>
        <div class="field-row">
          <div class="field"><label>First Name</label><input type="text" name="first_name" value="{{.UserSettings.FirstName}}"></div>
          <div class="field"><label>Last Name</label><input type="text" name="last_name" value="{{.UserSettings.LastName}}"></div>
        </div>
        <div class="field"><label>Profile Picture URL</label><input type="url" name="profile_picture" value="{{.UserSettings.ProfilePicture}}" placeholder="https://example.com/avatar.jpg"></div>
      </div>
      <div class="card">
        <h2>Profile Details</h2>
        <div class="field-row">
          <div class="field"><label>Job Title</label><input type="text" name="job_title" value="{{.UserSettings.JobTitle}}"></div>
          <div class="field"><label>Office Location</label><input type="text" name="dept" value="{{.UserSettings.Dept}}"></div>
        </div>
        <div class="field"><label>Phone</label><input type="tel" name="phone" value="{{.UserSettings.Phone}}" placeholder="+49 123 456789"></div>
        <div class="field"><label>Address</label><input type="text" name="address" value="{{.UserSettings.Address}}" placeholder="Street, City, Country"></div>
        <div class="field"><label>Messenger</label><input type="text" name="messenger" value="{{.UserSettings.Messenger}}" placeholder="@user:signal / t.me/user"></div>
        <div class="field"><label>Manager</label><select name="manager"><option value="">— No manager —</option>{{range .AllUsers}}<option value="{{.Username}}" {{if eq $.UserSettings.Manager .Username}}selected{{end}}>{{.DisplayName}} (@{{.Username}})</option>{{end}}</select></div>
        <div class="field"><label>Bio</label><textarea name="bio" rows="3" style="width:100%;padding:0.5rem;border:1px solid #e2e8f0;border-radius:6px;font-size:0.88rem;">{{.UserSettings.Bio}}</textarea></div>
      </div>
      <div class="card">
        <h2>Mail</h2>
        <p class="field-note">Server: {{.Settings.IMAP.Host}}:{{.Settings.IMAP.Port}}</p>
        <div class="field"><label>Email Address</label><input type="email" name="email" value="{{.UserSettings.Email}}"></div>
        <div class="field"><label>Email Password</label><input type="password" name="email_password" value="{{.UserSettings.EmailPassword}}"></div>
      </div>
      <div class="card">
        <h2>Preferences</h2>
        <div class="field-row">
          <div class="field">
            <label>Language</label>
            <select name="language">
              <option value="">{{.Settings.Company.Language}} (System default)</option>
              <option value="en" {{if eq .UserSettings.Language "en"}}selected{{end}}>English</option>
              <option value="de" {{if eq .UserSettings.Language "de"}}selected{{end}}>Deutsch</option>
            </select>
          </div>
          <div class="field">
            <label>Timezone</label>
            <select name="timezone">
              <option value="">{{.Settings.Company.Timezone}} (System default)</option>
              <option value="UTC">UTC</option>
              <option value="Europe/London">Europe/London</option>
              <option value="Europe/Berlin">Europe/Berlin</option>
              <option value="Europe/Paris">Europe/Paris</option>
              <option value="America/New_York">America/New_York</option>
              <option value="Asia/Tokyo">Asia/Tokyo</option>
            </select>
          </div>
        </div>
      </div>
      <div class="actions">
        <button type="submit" class="btn btn-primary">{{t .Lang "save"}}</button>
        <span id="savemsg" class="saved-msg">{{t .Lang "saved"}}</span>
      </div>
    </form>
    {{if .IsAdmin}}
    <div class="card">
      <h2>Administration</h2>
      <p style="color:#718096;font-size:0.9rem;">Manage users, groups, and workspace configuration.</p>
      <a href="/config" style="color:#63b3ed;text-decoration:none;font-size:0.9rem;">&#9878; Admin Panel →</a>
    </div>
    {{end}}
  </div>
  <script>
    async function saveSettings(e) {
      e.preventDefault();
      const form = document.getElementById('settings-form');
      const data = new FormData(form);
      const resp = await fetch('/settings/save', {method:'POST', body:new URLSearchParams(data)});
      const msg = document.getElementById('savemsg');
      if (resp.ok) { msg.style.display = 'inline'; setTimeout(() => msg.style.display = 'none', 3000); }
      return false;
    }
  </script>
</body>
</html>`

const peopleHTML = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>People — NextWorkspace</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; color: #1a1a2e; }
    header { background: linear-gradient(135deg, #1a1a2e, #16213e); color: #fff; padding: 1rem 1.5rem; display: flex; justify-content: space-between; align-items: center; }
    header h1 { font-size: 1.2rem; }
    header a { color: #63b3ed; text-decoration: none; font-size: 0.85rem; }
    .container { max-width: 1000px; margin: 0 auto; padding: 2rem; }
    input.search { width: 100%; padding: 0.75rem 1rem; border: 1px solid #e2e8f0; border-radius: 10px; font-size: 0.95rem; outline: none; margin-bottom: 1.5rem; }
    input.search:focus { border-color: #63b3ed; box-shadow: 0 0 0 2px rgba(99,179,237,0.15); }
    .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1rem; }
    .card { background: #fff; border-radius: 12px; padding: 1.5rem; text-align: center; text-decoration: none; color: inherit; box-shadow: 0 2px 8px rgba(0,0,0,0.08); transition: transform .2s; display: block; }
    .card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.12); }
    .avatar { width: 64px; height: 64px; border-radius: 50%; object-fit: cover; margin: 0 auto 0.75rem; display: block; }
    .avatar.initials { background: #1a1a2e; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; font-weight: 600; }
    .name { font-weight: 600; margin-bottom: 0.25rem; }
    .title { color: #718096; font-size: 0.85rem; }
    .dept { color: #a0aec0; font-size: 0.8rem; }
  </style>
</head>
<body>
  <header><h1>People</h1><a href="/home/">Back to Launcher</a></header>
  <div class="container">
    <input class="search" type="text" id="search" placeholder="Search by name or department..." oninput="filter(this.value)">
    <div class="grid" id="grid">
      {{range .People}}
        <a class="card" href="/people/{{.Username}}">
          {{if .ProfilePic}}<img src="{{.ProfilePic}}" class="avatar">{{else}}<div class="avatar initials">{{initial .DisplayName}}</div>{{end}}
          <div class="name">{{.DisplayName}}</div>
          {{if .JobTitle}}<div class="title">{{.JobTitle}}</div>{{end}}
          {{if .Dept}}<div class="dept">{{.Dept}}</div>{{end}}
        </a>
      {{end}}
    </div>
  </div>
  <script>
    function filter(q) { q=q.toLowerCase(); document.querySelectorAll('.card').forEach(c => { c.style.display = c.textContent.toLowerCase().includes(q) ? '' : 'none'; }); }
  </script>
</body>
</html>`

const profileHTML = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Profile — NextWorkspace</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; color: #1a1a2e; }
    header { background: linear-gradient(135deg, #1a1a2e, #16213e); color: #fff; padding: 1rem 1.5rem; display: flex; justify-content: space-between; align-items: center; }
    header h1 { font-size: 1.2rem; }
    header a { color: #63b3ed; text-decoration: none; font-size: 0.85rem; }
    .container { max-width: 700px; margin: 2rem auto; padding: 0 1rem; }
    .back { color: #63b3ed; text-decoration: none; font-size: 0.9rem; display: inline-block; margin-bottom: 1.5rem; }
    .header { display: flex; gap: 1.5rem; align-items: center; margin-bottom: 2rem; }
    .avatar { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; }
    .avatar.initials { background: #1a1a2e; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 2rem; font-weight: 600; min-width: 80px; }
    .header h2 { font-size: 1.5rem; margin-bottom: 0.25rem; }
    .header .sub { color: #718096; font-size: 0.9rem; }
    .card { background: #fff; border-radius: 10px; padding: 1.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.05); border: 1px solid #edf2f7; }
    .row { padding: 0.75rem 0; border-bottom: 1px solid #f7fafc; display: flex; gap: 1rem; }
    .row:last-child { border: none; }
    .row .label { width: 100px; color: #718096; font-weight: 500; font-size: 0.85rem; flex-shrink: 0; }
    .row .value { color: #4a5568; font-size: 0.9rem; }
    .row a { color: #63b3ed; text-decoration: none; }
    .row a:hover { text-decoration: underline; }
    .bio { line-height: 1.6; color: #4a5568; font-size: 0.9rem; }
  </style>
</head>
<body>
  <header><h1>Profile</h1><a href="/people">Back to People</a></header>
  <div class="container">
    <a class="back" href="/people">← All People</a>
    <div class="header">
      {{if .Profile.ProfilePicture}}<img src="{{.Profile.ProfilePicture}}" class="avatar">{{else}}<div class="avatar initials">{{initial .Profile.FirstName}} {{initial .Profile.LastName}}</div>{{end}}
      <div>
        <h2>{{.Profile.FirstName}} {{.Profile.LastName}}</h2>
        {{if .Profile.JobTitle}}<div class="sub">{{.Profile.JobTitle}}</div>{{end}}
        {{if .Profile.Dept}}<div class="sub">{{.Profile.Dept}}</div>{{end}}
      </div>
    </div>
    <div class="card">
      {{if .Profile.Email}}<div class="row"><div class="label">Email</div><div class="value"><a href="mailto:{{.Profile.Email}}">{{.Profile.Email}}</a></div></div>{{end}}
      {{if .Profile.Phone}}<div class="row"><div class="label">Phone</div><div class="value"><a href="tel:{{.Profile.Phone}}">{{.Profile.Phone}}</a></div></div>{{end}}
      {{if .Profile.Address}}<div class="row"><div class="label">Address</div><div class="value">{{.Profile.Address}}</div></div>{{end}}
      {{if .Profile.Messenger}}<div class="row"><div class="label">Messenger</div><div class="value">{{.Profile.Messenger}}</div></div>{{end}}
      {{if .ManagerName}}<div class="row"><div class="label">Manager</div><div class="value"><a href="/people/{{.Profile.Manager}}">{{.ManagerName}}</a></div></div>{{end}}
      {{if .Profile.Bio}}<div class="row"><div class="label">Bio</div><div class="value bio">{{.Profile.Bio}}</div></div>{{end}}
    </div>
  </div>
</body>
</html>`

const globalSettingsHTML = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Global Settings — NextWorkspace</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; color: #1a1a2e; min-height: 100vh; }
    .topbar { background: linear-gradient(135deg, #1a1a2e, #16213e); color: #fff; padding: 0.75rem 1.5rem; display: flex; justify-content: space-between; align-items: center; }
    .topbar h1 { font-size: 1.1rem; font-weight: 600; }
    .topbar a { color: #a0aec0; text-decoration: none; font-size: 0.85rem; }
    .topbar a:hover { color: #fff; }
    .container { max-width: 720px; margin: 2rem auto; padding: 0 1rem; }
    .banner { padding: 0.75rem 1rem; border-radius: 8px; margin-bottom: 1.25rem; font-size: 0.88rem; }
    .banner-success { background: #c6f6d5; color: #276749; border: 1px solid #9ae6b4; }
    .card { background: #fff; border-radius: 10px; padding: 1.5rem; margin-bottom: 1.25rem; box-shadow: 0 1px 3px rgba(0,0,0,0.05); border: 1px solid #edf2f7; }
    .card h2 { font-size: 1rem; font-weight: 600; color: #2d3748; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid #edf2f7; }
    .field { margin-bottom: 0.9rem; }
    .field label { display: block; font-size: 0.82rem; font-weight: 500; color: #4a5568; margin-bottom: 0.25rem; }
    .field input, .field select { width: 100%; padding: 0.5rem 0.7rem; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 0.88rem; color: #4a5568; background: #fff; outline: none; }
    .field input:focus, .field select:focus { border-color: #63b3ed; box-shadow: 0 0 0 2px rgba(99,179,237,0.15); }
    .field .note { color: #a0aec0; font-size: 0.78rem; margin-top: 0.2rem; }
    .field-row { display: flex; gap: 1rem; }
    .field-row .field { flex: 1; }
    .info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
    .info-item { padding: 0.5rem 0; border-bottom: 1px solid #f7fafc; }
    .info-item .label { font-size: 0.78rem; color: #a0aec0; }
    .info-item .value { font-size: 0.9rem; color: #4a5568; }
    .status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 0.35rem; }
    .status-up { background: #48bb78; }
    .status-down { background: #fc8181; }
    .btn { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.88rem; font-weight: 500; cursor: pointer; border: none; text-decoration: none; }
    .btn-primary { background: #1a1a2e; color: #fff; }
    .btn-primary:hover { background: #2d3748; }
    .btn-ghost { background: transparent; color: #718096; border: 1px solid #e2e8f0; }
    .btn-ghost:hover { background: #f7fafc; }
    .actions { display: flex; gap: 0.75rem; margin-top: 1.5rem; }
  </style>
</head>
<body>
  <div class="topbar">
    <h1>NextWorkspace · Global Settings</h1>
    <a href="/config">Back to Admin</a>
  </div>
  <div class="container">
    {{if .Saved}}<div class="banner banner-success">Settings saved. SMTP changes require applying — click "Apply Settings" below.</div>{{end}}
    <form method="POST" action="/config/global/save">
      <div class="card">
        <h2>Company</h2>
        <div class="field">
          <label>Company Name</label>
          <input name="company_name" value="{{.CompanyName}}" placeholder="NextWorkspace">
        </div>
        <div class="field-row">
          <div class="field">
            <label>Default Language</label>
            <select name="language">
              <option value="en" {{if eq .Language "en"}}selected{{end}}>English</option>
              <option value="de" {{if eq .Language "de"}}selected{{end}}>Deutsch</option>
            </select>
          </div>
          <div class="field">
            <label>Timezone</label>
            <select name="timezone">
              <option value="UTC" {{if eq .Timezone "UTC"}}selected{{end}}>UTC</option>
              <option value="Europe/London" {{if eq .Timezone "Europe/London"}}selected{{end}}>Europe/London</option>
              <option value="Europe/Berlin" {{if eq .Timezone "Europe/Berlin"}}selected{{end}}>Europe/Berlin</option>
              <option value="America/New_York" {{if eq .Timezone "America/New_York"}}selected{{end}}>America/New_York</option>
              <option value="America/Los_Angeles" {{if eq .Timezone "America/Los_Angeles"}}selected{{end}}>America/Los_Angeles</option>
              <option value="Asia/Tokyo" {{if eq .Timezone "Asia/Tokyo"}}selected{{end}}>Asia/Tokyo</option>
            </select>
          </div>
        </div>
      </div>
      <div class="card">
        <h2>SMTP</h2>
        <div class="field">
          <label>SMTP Host</label>
          <input name="smtp_host" value="{{.SMTPHost}}" placeholder="smtp.openxchange.eu">
        </div>
        <div class="field-row">
          <div class="field">
            <label>SMTP Port</label>
            <input name="smtp_port" value="{{.SMTPPort}}" placeholder="587">
          </div>
          <div class="field">
            <label>SMTP User</label>
            <input name="smtp_user" value="{{.SMTPUser}}" placeholder="post@nextwks.eu">
          </div>
        </div>
        <div class="field-row">
          <div class="field">
            <label>SMTP Password</label>
            <input name="smtp_password" type="password" placeholder="Leave blank to keep current">
            <div class="note">Leave blank unless changing the password.</div>
          </div>
          <div class="field">
            <label>Sender Email</label>
            <input name="smtp_sender" value="{{.SMTPSender}}" placeholder="post@nextwks.eu">
          </div>
        </div>
      </div>
      <div class="card">
        <h2>System (read-only)</h2>
        <div class="info-grid">
          <div class="info-item"><div class="label">Domain</div><div class="value">{{.Domain}}</div></div>
          <div class="info-item"><div class="label">Admin Email</div><div class="value">{{.AdminEmail}}</div></div>
          <div class="info-item">
            <div class="label">Authelia</div>
            <div class="value">{{if .AutheliaUp}}<span class="status-dot status-up"></span>Running{{else}}<span class="status-dot status-down"></span>Unreachable{{end}}</div>
          </div>
          <div class="info-item"><div class="label">Version</div><div class="value">0.1.0.0011</div></div>
        </div>
      </div>
      <div class="actions">
        <button type="submit" class="btn btn-primary">Save Settings</button>
        <button type="button" class="btn btn-ghost" onclick="applySettings()">Apply Settings</button>
      </div>
    </form>
  </div>
  <script>
    async function applySettings() {
      const btn = event.target; btn.textContent = 'Applying...'; btn.disabled = true;
      const resp = await fetch('/config/apply', {method:'POST'});
      const result = await resp.json();
      if (result.success) { alert('Settings applied. Containers restarted.'); }
      else { alert('Error: ' + JSON.stringify(result)); }
      btn.textContent = 'Apply Settings'; btn.disabled = false;
    }
  </script>
</body>
</html>`

const adminHTML = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Admin Panel — NextWorkspace</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; color: #1a1a2e; height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
    header { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); color: #fff; padding: 0.75rem 1.5rem; display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; }
    header h1 { font-size: 1.1rem; font-weight: 600; }
    header .top-nav { display: flex; gap: 1.5rem; align-items: center; }
    header .top-nav a { color: #a0aec0; text-decoration: none; font-size: 0.85rem; transition: color .15s; }
    header .top-nav a:hover { color: #fff; }
    .layout { display: flex; flex: 1; overflow: hidden; }
    .sidebar { width: 250px; background: #fff; border-right: 1px solid #e2e8f0; display: flex; flex-direction: column; flex-shrink: 0; overflow-y: auto; }
    .sidebar .section-label { padding: 1.25rem 1.25rem 0.4rem; font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #a0aec0; }
    .sidebar a { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 1.25rem; color: #4a5568; text-decoration: none; font-size: 0.88rem; border-left: 3px solid transparent; transition: all .12s; }
    .sidebar a:hover { background: #f7fafc; border-left-color: #63b3ed; color: #1a1a2e; }
    .sidebar a.active { background: #ebf4ff; border-left-color: #1a1a2e; color: #1a1a2e; font-weight: 600; }
    .sidebar a .icon { width: 1.25rem; text-align: center; font-size: 1rem; opacity: .7; }
    .sidebar a.disabled { opacity: .35; cursor: not-allowed; pointer-events: none; }
    .sidebar .spacer { flex: 1; }
    .sidebar .footer { padding: 1rem 1.25rem; border-top: 1px solid #e2e8f0; font-size: 0.75rem; color: #a0aec0; }
    .content { flex: 1; padding: 1.5rem 2rem; overflow-y: auto; background: #f8fafc; }
    .page-header { margin-bottom: 1.5rem; }
    .page-header h2 { font-size: 1.4rem; font-weight: 700; color: #1a1a2e; margin-bottom: 0.25rem; }
    .page-header p { color: #718096; font-size: 0.9rem; }
    .card { background: #fff; border-radius: 10px; padding: 1.5rem; margin-bottom: 1.25rem; box-shadow: 0 1px 3px rgba(0,0,0,0.05); border: 1px solid #edf2f7; }
    .card h3 { font-size: 1rem; font-weight: 600; color: #2d3748; margin-bottom: 1rem; }
    table { width: 100%; border-collapse: collapse; }
    th { text-align: left; padding: 0.55rem 0.75rem; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: #718096; border-bottom: 2px solid #edf2f7; }
    td { padding: 0.65rem 0.75rem; font-size: 0.88rem; border-bottom: 1px solid #f7fafc; color: #4a5568; }
    tr:hover td { background: #f7fafc; }
    .badge { display: inline-block; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 500; background: #edf2f7; color: #4a5568; margin-right: 0.2rem; }
    .badge-green { background: #c6f6d5; color: #276749; }
    .badge-red { background: #fed7d7; color: #c53030; }
    .btn { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.45rem 0.9rem; border-radius: 6px; font-size: 0.85rem; font-weight: 500; cursor: pointer; border: none; text-decoration: none; transition: all .12s; }
    .btn-primary { background: #1a1a2e; color: #fff; }
    .btn-primary:hover { background: #2d3748; }
    .btn-danger { background: #fff; color: #e53e3e; border: 1px solid #fed7d7; }
    .btn-danger:hover { background: #fff5f5; }
    .btn-ghost { background: transparent; color: #718096; border: 1px solid #e2e8f0; }
    .btn-ghost:hover { background: #f7fafc; }
    .btn-sm { padding: 0.3rem 0.6rem; font-size: 0.8rem; }
    input, select { padding: 0.45rem 0.7rem; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 0.88rem; color: #4a5568; background: #fff; outline: none; transition: border .12s; }
    input:focus, select:focus { border-color: #63b3ed; box-shadow: 0 0 0 2px rgba(99,179,237,0.15); }
    .field { margin-bottom: 0.9rem; }
    .field label { display: block; font-size: 0.82rem; font-weight: 500; color: #4a5568; margin-bottom: 0.25rem; }
    .field input, .field select { width: 100%; }
    .field-row { display: flex; gap: 1rem; }
    .field-row .field { flex: 1; }
    .form-row { display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; margin-bottom: 1rem; }
    .info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
    .info-item { padding: 0.5rem 0; border-bottom: 1px solid #f7fafc; font-size: 0.9rem; }
    .info-item .label { font-size: 0.78rem; color: #a0aec0; }
    .info-item .value { font-size: 0.9rem; color: #4a5568; }
    .status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 0.35rem; }
    .status-up { background: #48bb78; }
    .status-down { background: #fc8181; }
    .actions { display: flex; gap: 0.75rem; align-items: center; margin-top: 0.5rem; }
    .success { background: #c6f6d5; color: #276749; padding: 0.75rem 1rem; border-radius: 8px; margin-bottom: 1rem; font-size: 0.88rem; border: 1px solid #9ae6b4; }
    .error { background: #fed7d7; color: #c53030; padding: 0.75rem 1rem; border-radius: 8px; margin-bottom: 1rem; font-size: 0.88rem; border: 1px solid #feb2b2; }
    .password-box { background: #1a1a2e; color: #63b3ed; padding: 0.65rem 1rem; border-radius: 6px; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.85rem; margin-top: 0.5rem; display: inline-block; }
    .hidden { display: none; }
    .empty-state { text-align: center; padding: 2.5rem 1rem; color: #a0aec0; }
    .empty-state .icon { font-size: 2.5rem; margin-bottom: 0.75rem; }
    .empty-state p { font-size: 0.9rem; }
  </style>
</head>
<body>
  <header>
    <h1>NextWorkspace · Admin <span style="font-size:0.7rem;color:#718096;font-weight:400;">v{{.Version}}</span></h1>
    <div class="top-nav">
      <a href="/home/">Launcher</a>
      <a href="https://auth.nextwks.eu/logout" style="color:#fc8181;">Logout</a>
    </div>
  </header>
  <div class="layout">
    <nav class="sidebar">
      <div class="section-label">Core Settings</div>
      <a href="#" class="active" data-section="global" onclick="return switchSection('global')"><span class="icon">&#9881;</span> {{t .Lang "nav_global"}}</a>
      <a href="#" data-section="access" onclick="return switchSection('access')"><span class="icon">&#128101;</span> {{t .Lang "nav_access"}}</a>
      <a href="#" data-section="security" onclick="return switchSection('security')"><span class="icon">&#128274;</span> {{t .Lang "nav_security"}}</a>
      <a href="#" data-section="domain" onclick="return switchSection('domain')"><span class="icon">&#127760;</span> {{t .Lang "nav_domain"}}</a>

      <div class="section-label">App Settings</div>
      <a href="#" class="disabled"><span class="icon">&#128230;</span> {{t .Lang "nav_mail"}}</a>
      <a href="#" class="disabled"><span class="icon">&#128196;</span> {{t .Lang "nav_docs"}}</a>
      <a href="#" class="disabled"><span class="icon">&#128197;</span> {{t .Lang "nav_calendar"}}</a>

      <div class="spacer"></div>
      <div class="footer">NextWorkspace v0.1.0.0011</div>
    </nav>

    <main class="content">
      {{if .ApiError}}<div class="error">Could not connect to authelia-api on :8080</div>{{end}}

      <!-- Global -->
      <div id="page-global" class="page">
        <div class="page-header"><h2>Global Settings</h2><p>General system configuration for your workspace.</p></div>
        <form id="global-form" onsubmit="return saveGlobal(event)">
          <div class="card">
            <h3>Company</h3>
            <div class="field"><label>{{t .Lang "company_name"}}</label><input type="text" name="company_name" value="{{.Settings.Company.Name}}"></div>
            <div class="field"><label>{{t .Lang "company_subtitle"}}</label><input type="text" name="company_subtitle" value="{{.Settings.Company.Subtitle}}"></div>
             <div class="field"><label>{{t .Lang "company_logo"}}</label><input type="url" name="company_logo" value="{{.Settings.Company.Logo}}" placeholder="https://example.com/logo.png"></div>
             <div class="field"><label>Top-Level User (CEO)</label><select name="top_user"><option value="">— Select —</option>{{range .AllUsers}}<option value="{{.Username}}" {{if eq $.Settings.Company.TopUser .Username}}selected{{end}}>{{.DisplayName}} (@{{.Username}})</option>{{end}}</select></div>
             <div class="field-row">
              <div class="field"><label>{{t .Lang "language"}}</label><select name="language"><option value="en" {{if eq .Settings.Company.Language "en"}}selected{{end}}>English</option><option value="de" {{if eq .Settings.Company.Language "de"}}selected{{end}}>Deutsch</option></select></div>
              <div class="field"><label>{{t .Lang "timezone"}}</label><select name="timezone">{{$tz := .Settings.Company.Timezone}}<option value="UTC" {{if eq $tz "UTC"}}selected{{end}}>UTC</option><option value="Europe/London" {{if eq $tz "Europe/London"}}selected{{end}}>Europe/London</option><option value="Europe/Berlin" {{if eq $tz "Europe/Berlin"}}selected{{end}}>Europe/Berlin</option><option value="America/New_York" {{if eq $tz "America/New_York"}}selected{{end}}>America/New_York</option><option value="Asia/Tokyo" {{if eq $tz "Asia/Tokyo"}}selected{{end}}>Asia/Tokyo</option></select></div>
            </div>
          </div>
          <div class="card">
            <h3>SMTP</h3>
            <div class="field"><label>SMTP Host</label><input type="text" name="smtp_host" value="{{.Settings.SMTP.Host}}" placeholder="smtp.openxchange.eu"></div>
            <div class="field-row">
              <div class="field"><label>SMTP Port</label><input type="number" name="smtp_port" value="{{.Settings.SMTP.Port}}" placeholder="587"></div>
              <div class="field"><label>SMTP User</label><input type="text" name="smtp_user" value="{{.Settings.SMTP.User}}" placeholder="post@nextwks.eu"></div>
            </div>
            <div class="field-row">
              <div class="field"><label>SMTP Password</label><input type="password" name="smtp_password" placeholder="Leave blank to keep current"></div>
              <div class="field"><label>Sender Email</label><input type="email" name="smtp_sender" value="{{.Settings.SMTP.Sender}}" placeholder="post@nextwks.eu"></div>
            </div>
          </div>
          <div class="card">
            <h3>IMAP (Mail Client)</h3>
            <div class="field-row">
              <div class="field"><label>IMAP Host</label><input type="text" name="imap_host" value="{{.Settings.IMAP.Host}}" placeholder="imap.openxchange.eu"></div>
              <div class="field"><label>IMAP Port</label><input type="number" name="imap_port" value="{{.Settings.IMAP.Port}}" placeholder="993"></div>
            </div>
          </div>
          <div class="card">
            <h3>System (read-only)</h3>
            <div class="info-grid">
              <div class="info-item"><div class="label">Domain</div><div class="value">{{.Domain}}</div></div>
              <div class="info-item"><div class="label">Admin Email</div><div class="value">{{.AdminEmail}}</div></div>
              <div class="info-item"><div class="label">Caddy</div><div class="value">{{if .CaddyUp}}<span class="status-dot status-up"></span>Running{{else}}<span class="status-dot status-down"></span>Down{{end}}</div></div>
              <div class="info-item"><div class="label">Authelia</div><div class="value">{{if .AutheliaUp}}<span class="status-dot status-up"></span>Running{{else}}<span class="status-dot status-down"></span>Down{{end}}</div></div>
              <div class="info-item"><div class="label">Launcher</div><div class="value">{{if .LauncherUp}}<span class="status-dot status-up"></span>Running{{else}}<span class="status-dot status-down"></span>Down{{end}}</div></div>
            </div>
          </div>
          <div class="actions"><button type="submit" class="btn btn-primary">Save Settings</button><span id="savemsg" style="display:none;color:#48bb78;font-size:0.9rem;">Saved</span></div>
        </form>
      </div>

      <!-- Access (Users) -->
      <div id="page-access" class="page hidden">
        <div class="page-header" style="display:flex;justify-content:space-between;align-items:center;">
          <div><h2>Access Management</h2><p>Manage users, groups, and authentication policies.</p></div>
          <button class="btn btn-primary" onclick="showCreateModal()">+ Add User</button>
        </div>
        <div style="background:#fff;border-radius:10px;border:1px solid #edf2f7;overflow:hidden;">
          <table style="width:100%;border-collapse:collapse;">
            <thead><tr style="background:#f7fafc;"><th style="padding:12px 16px;text-align:left;font-size:0.85rem;color:#718096;text-transform:uppercase;">Username</th><th style="padding:12px 16px;text-align:left;font-size:0.85rem;color:#718096;text-transform:uppercase;">Display Name</th><th style="padding:12px 16px;text-align:left;font-size:0.85rem;color:#718096;text-transform:uppercase;">Email</th><th style="padding:12px 16px;text-align:left;font-size:0.85rem;color:#718096;text-transform:uppercase;">Groups</th><th style="padding:12px 16px;text-align:left;font-size:0.85rem;color:#718096;text-transform:uppercase;">Status</th><th style="padding:12px 16px;text-align:left;font-size:0.85rem;color:#718096;text-transform:uppercase;">Actions</th></tr></thead>
            <tbody id="usersTable"></tbody>
          </table>
        </div>
      </div>

      <!-- Create User Modal -->
      <div id="createModal" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:1000;">
        <div style="background:#fff;border-radius:12px;padding:2rem;width:500px;max-width:90%;margin:5vh auto;">
          <h3 style="margin-bottom:1.5rem;">Add User</h3>
          <form id="createForm" onsubmit="return createUser(event)">
            <div class="field"><label>Username</label><input type="text" name="username" required pattern="[a-z0-9._-]+"></div>
            <div class="field"><label>Display Name</label><input type="text" name="display_name" required></div>
            <div class="field"><label>Email</label><input type="email" name="email" required></div>
            <div class="field"><label>Groups</label>
              <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:0.4rem;">
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="users" checked> Users</label>
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="drive"> Drive</label>
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="office"> Office</label>
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="erp"> ERP</label>
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="chat"> Chat</label>
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="meet"> Meet</label>
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="mail"> Mail</label>
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="ai"> AI</label>
                <label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="groups" value="admins"> Admin</label>
              </div>
            </div>
            <div style="display:flex;gap:0.75rem;margin-top:1.5rem;">
              <button type="submit" class="btn btn-primary">Create</button>
              <button type="button" class="btn btn-ghost" onclick="closeCreateModal()">Cancel</button>
            </div>
          </form>
          <div id="createResult"></div>
        </div>
      </div>

      <!-- Security -->
      <div id="page-security" class="page hidden">
        <div class="page-header"><h2>Security</h2><p>Authentication policies, tokens, and session configuration.</p></div>
        <div class="card"><h3>Authentication</h3><p style="color:#718096;font-size:0.9rem;">Configured via Authelia. Policies enforced at the proxy level by Caddy.</p></div>
      </div>

      <!-- Domain -->
      <div id="page-domain" class="page hidden">
        <div class="page-header"><h2>Domain</h2><p>Domain mapping, email configuration, and network settings.</p></div>
        <div class="card"><h3>Domain Configuration</h3><p style="color:#718096;font-size:0.9rem;">Managed through Caddyfile and Authelia configuration.</p></div>
      </div>
    </main>
  </div>

  <script>
    let currentSection = 'global';

    function switchSection(name) {
      currentSection = name;
      document.querySelectorAll('.sidebar a').forEach(a => a.classList.remove('active'));
      document.querySelector('[data-section="' + name + '"]').classList.add('active');
      document.querySelectorAll('.page').forEach(p => p.classList.add('hidden'));
      const page = document.getElementById('page-' + name);
      if (page) page.classList.remove('hidden');
      if (name === 'access') loadUsers();
      if (name === 'global') loadGlobal();
      return false;
    }

    async function loadGlobal() {
      const resp = await fetch('/config?format=settings');
      if (!resp.ok) return;
      const s = await resp.json();
      document.querySelector('[name="company_name"]').value = s.company_name || '';
      document.querySelector('[name="language"]').value = s.language || 'en';
      document.querySelector('[name="timezone"]').value = s.timezone || 'UTC';
      document.querySelector('[name="smtp_host"]').value = s.smtp_host || '';
      document.querySelector('[name="smtp_port"]').value = s.smtp_port || '587';
      document.querySelector('[name="smtp_user"]').value = s.smtp_user || '';
      document.querySelector('[name="smtp_sender"]').value = s.smtp_sender || '';
      document.querySelector('[name="imap_host"]').value = s.imap_host || '';
      document.querySelector('[name="imap_port"]').value = s.imap_port || '993';
    }

    async function saveGlobal(e) {
      e.preventDefault();
      const form = document.getElementById('global-form');
      const data = new FormData(form);
      const resp = await fetch('/config/global/save', {method:'POST', body:new URLSearchParams(data)});
      const msg = document.getElementById('savemsg');
      if (resp.ok) {
        msg.style.display = 'inline';
        setTimeout(() => msg.style.display = 'none', 3000);
      }
      return false;
    }

    function esc(t) { var d=document.createElement('div'); d.textContent=t; return d.innerHTML; }

    async function loadUsers() {
      const resp = await fetch('/api/users');
      if (!resp.ok) return;
      const users = await resp.json();
      const tbody = document.getElementById('usersTable');
      if (!users || users.length === 0) { tbody.innerHTML = '<tr><td colspan="6" style="padding:2rem;text-align:center;color:#a0aec0;">No users found.</td></tr>'; return; }
      tbody.innerHTML = users.map(u => {
        const groups = (u.groups||[]).map(g => '<span class="badge">' + esc(g) + '</span>').join(' ');
        const status = u.disabled ? '<span style="color:#e53e3e;font-weight:500;">Disabled</span>' : '<span style="color:#38a169;font-weight:500;">Active</span>';
        const del = u.username === 'master' ? '<button class="btn btn-danger btn-sm" disabled title="Cannot delete master">Delete</button>' : '<button class="btn btn-danger btn-sm" onclick="deleteUser(\'' + u.username + '\')">Delete</button>';
        return '<tr><td style="padding:12px 16px;border-top:1px solid #edf2f7;"><strong>' + esc(u.username) + '</strong></td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + esc(u.display_name||'') + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + esc(u.email||'') + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + groups + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + status + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + del + '</td></tr>';
      }).join('');
    }

    async function createUser(e) {
      e.preventDefault();
      const form = document.getElementById('createForm');
      const data = new FormData(form);
      const chk = Array.from(form.querySelectorAll('input[name="groups"]:checked')).map(c => c.value);
      const body = JSON.stringify({users:[{username:data.get('username'),display_name:data.get('display_name'),email:data.get('email'),groups:chk.length?chk:['users']}]});
      const resp = await fetch('/api/users/bulk', {method:'POST', headers:{'Content-Type':'application/json'}, body});
      if (resp.ok) {
        const result = await resp.json();
        if (result.users && result.users[0] && result.users[0].placeholder_password) {
          alert('User created!\n\nUsername: ' + result.users[0].username + '\nPassword: ' + result.users[0].placeholder_password);
        }
        closeCreateModal();
        form.reset();
        loadUsers();
      } else {
        alert('Failed to create user');
      }
      return false;
    }

    async function deleteUser(username) {
      if (!confirm('Delete user "' + username + '"?')) return;
      const resp = await fetch('/api/users/' + username, {method:'DELETE'});
      if (resp.ok) loadUsers();
      else alert('Failed to delete user');
    }

    function showCreateModal() { document.getElementById('createModal').style.display = 'block'; }
    function closeCreateModal() { document.getElementById('createModal').style.display = 'none'; }

    if (document.querySelector('[data-section="access"].active')) loadUsers();
  </script>
</body>
</html>`

// --- Main ---

func main() {
	configDir := os.Getenv("CONFIG_DIR")
	if configDir == "" {
		configDir = "/opt/nextworkspace/config/nextworkspace"
	}

	cfg, err := loadConfig(configDir)
	if err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}

	apps, err := loadApps(configDir)
	if err != nil {
		log.Fatalf("Failed to load apps: %v", err)
	}

	settings, _ := loadSettings(configDir)
	companyName := "NextWorkspace"
	if settings != nil && settings.Company.Name != "" {
		companyName = settings.Company.Name
	}

	// Load version
	versionBytes, _ := os.ReadFile("/opt/nextworkspace/VERSION")
	v := strings.TrimSpace(string(versionBytes))
	if v != "" {
		appVersion = v
	}

	// Load translations from lng/ directory
	loadTranslations("/opt/nextworkspace/lng")

	// Create template FuncMap for translations
	funcMap = template.FuncMap{"t": t}

	// Parse templates with FuncMap
	launcherTmpl = template.Must(template.New("launcher").Funcs(funcMap).Parse(launcherHTML))
	adminTmpl = template.Must(template.New("admin").Funcs(funcMap).Parse(adminHTML))

	addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)

	mux := http.NewServeMux()

	// Public
	mux.HandleFunc("/health", healthHandler)
	mux.HandleFunc("/api/settings/public", publicSettingsHandler)

	// Protectected: launcher
	if companyName != "" {
		cfg.App.Name = companyName
	}
	mux.Handle("/home/", authMiddleware(launcherHandler(cfg, apps)))
	mux.Handle("/home", authMiddleware(launcherHandler(cfg, apps)))

	// Protected: settings
	mux.Handle("/settings", authMiddleware(settingsHandler))
	mux.Handle("/settings/save", authMiddleware(http.HandlerFunc(userSettingsSaveHandler)))

	// People directory
	mux.Handle("/people", authMiddleware(http.HandlerFunc(peopleDirectoryHandler)))
	mux.Handle("/people/", authMiddleware(http.HandlerFunc(peopleDirectoryHandler)))

	// Protected: admin — admins only
	mux.Handle("/config", adminGroupMiddleware(authMiddleware(adminHandler)))
	mux.Handle("/config/", adminGroupMiddleware(authMiddleware(adminHandler)))

	// Global settings save — admins only
	mux.HandleFunc("/config/global/save", adminGroupMiddleware(authMiddleware(globalSettingsSaveHandler)))
	mux.HandleFunc("/config/apply", adminGroupMiddleware(authMiddleware(applySettingsHandler)))

	// API proxy — authelia-api (authenticated users only)
	mux.Handle("/api/", authMiddleware(apiProxyHandler))

	// Protected: upstream app proxies
	for _, app := range apps {
		if app.Path != "" && app.Upstream != "" && app.Path != "/config" && app.Path != "/home" {
			proxyHandler := authMiddleware(proxyToUpstream(app.Upstream))
			mux.Handle(app.Path+"/", proxyHandler)
			mux.Handle(app.Path, proxyHandler)
		}
	}

	// Default: www landing page or redirect to /home/
	domain := os.Getenv("DOMAIN")
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if strings.HasPrefix(r.Host, "www.") || (domain != "" && r.Host == "www."+domain) {
			w.Header().Set("Content-Type", "text/html; charset=utf-8")
			fmt.Fprint(w, landingPageHTML)
			return
		}
		http.Redirect(w, r, "/home/", http.StatusFound)
	})

	log.Printf("NextWorkspace listening on %s", addr)
	log.Fatal(http.ListenAndServe(addr, mux))
}
