package views

import (
	"embed"
	"encoding/json"
	"html/template"
	"net/http"
	"path"
	"strconv"
	"strings"
	"time"

	"maintainarr/internal/models"
)

//go:embed layouts/*.gohtml pages/*.gohtml
var templatesFS embed.FS

type Renderer struct {
	functions template.FuncMap
}

type ViewData struct {
	Title           string
	Shell           string
	PageTemplate    string
	ThemeClass      string
	ThemeMode       string
	CurrentPath     string
	Flash           string
	User            any
	Organization    any
	AvailableGroups any
	AvailableTags   any
	Content         any
}

func NewRenderer() (*Renderer, error) {
	functions := template.FuncMap{
		"icon":                    icon,
		"contains":                strings.Contains,
		"distroIconClass":         distroIconClass,
		"nodeIconClass":           nodeIconClass,
		"nodeIconPending":         nodeIconPending,
		"packageManagerIconClass": packageManagerIconClass,
		"packageManagerLabel":     packageManagerLabel,
		"groupIcons":              groupIcons,
		"hasUpdateDay":            hasUpdateDay,
		"updateWindowSummary":     updateWindowSummary,
		"updatePackages":          updatePackages,
		"uptime":                  formatUptime,
		"safeHTML":                func(value string) template.HTML { return template.HTML(value) },
		"nowYear":                 func() int { return time.Now().Year() },
		"lower":                   strings.ToLower,
		"splitLines": func(value string) []string {
			var lines []string
			for _, line := range strings.Split(value, "\n") {
				line = strings.TrimSpace(line)
				if line != "" {
					lines = append(lines, line)
				}
			}
			return lines
		},
		"seq": func(start, end int) []int {
			values := make([]int, 0, end-start+1)
			for i := start; i <= end; i++ {
				values = append(values, i)
			}
			return values
		},
	}

	return &Renderer{functions: functions}, nil
}

func (r *Renderer) Render(w http.ResponseWriter, name string, data ViewData) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	layout := "layouts/app.gohtml"
	if data.Shell == "auth" {
		layout = "layouts/auth.gohtml"
	} else if data.Shell == "console" {
		layout = "layouts/console.gohtml"
	}

	parsed, err := template.New("base").Funcs(r.functions).ParseFS(
		templatesFS,
		"layouts/base.gohtml",
		layout,
		path.Join("pages", name+".gohtml"),
	)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	_ = parsed.ExecuteTemplate(w, "base", data)
}

func (r *Renderer) RenderPageTemplate(w http.ResponseWriter, page, templateName string, data any) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	parsed, err := template.New("page").Funcs(r.functions).ParseFS(
		templatesFS,
		path.Join("pages", page+".gohtml"),
	)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	_ = parsed.ExecuteTemplate(w, templateName, data)
}

func formatUptime(seconds int64) string {
	days := seconds / 86400
	hours := (seconds % 86400) / 3600
	minutes := (seconds % 3600) / 60
	if days > 0 {
		return template.HTMLEscapeString(strings.TrimSpace(strings.Join([]string{
			formatPart(days, "d"),
			formatPart(hours, "h"),
			formatPart(minutes, "m"),
		}, " ")))
	}
	return template.HTMLEscapeString(strings.TrimSpace(strings.Join([]string{
		formatPart(hours, "h"),
		formatPart(minutes, "m"),
	}, " ")))
}

func formatPart(value int64, suffix string) string {
	if value == 0 {
		return ""
	}
	return strconv.FormatInt(value, 10) + suffix
}

func distroIconClass(distro string) string {
	value := strings.ToLower(strings.TrimSpace(distro))
	switch {
	case strings.Contains(value, "ubuntu"):
		return "fl-ubuntu"
	case strings.Contains(value, "debian"):
		return "fl-debian"
	case strings.Contains(value, "arch"), strings.Contains(value, "manjaro"), strings.Contains(value, "endeavouros"):
		return "fl-archlinux"
	default:
		return "ti ti-server-2"
	}
}

func nodeIconClass(distro, packageManager string) string {
	if className := distroIconClass(distro); className != "ti ti-server-2" {
		return className
	}

	switch strings.ToLower(strings.TrimSpace(packageManager)) {
	case "apt":
		return "fl-debian"
	case "pacman":
		return "fl-archlinux"
	default:
		return "ti ti-server-2"
	}
}

func nodeIconPending(distro, packageManager string) bool {
	distroValue := strings.ToLower(strings.TrimSpace(distro))
	packageValue := strings.ToLower(strings.TrimSpace(packageManager))
	return packageValue == "" && (distroValue == "" || distroValue == "linux")
}

func packageManagerIconClass(value string) string {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "apt":
		return "ti ti-brand-debian"
	case "pacman":
		return "ti ti-brand-archlinux"
	case "dnf", "yum", "zypper", "apk", "nix", "emerge":
		return "ti ti-package"
	default:
		return "ti ti-package"
	}
}

func packageManagerLabel(value string) string {
	trimmed := strings.TrimSpace(value)
	if trimmed == "" {
		return "Unknown"
	}
	switch strings.ToLower(trimmed) {
	case "apt":
		return "APT"
	case "dnf":
		return "DNF"
	case "yum":
		return "YUM"
	default:
		return strings.ToUpper(trimmed[:1]) + strings.ToLower(trimmed[1:])
	}
}

func updatePackages(value string) []models.NodeUpdatePackage {
	trimmed := strings.TrimSpace(value)
	if trimmed == "" {
		return nil
	}
	var packages []models.NodeUpdatePackage
	if err := json.Unmarshal([]byte(trimmed), &packages); err != nil {
		return nil
	}
	return packages
}

func groupIcons() []string {
	return []string{
		"ti ti-stack-2", "ti ti-server", "ti ti-server-2", "ti ti-server-bolt", "ti ti-server-cog", "ti ti-server-off",
		"ti ti-server-spark", "ti ti-cloud", "ti ti-cloud-bolt", "ti ti-cloud-cog", "ti ti-cloud-code", "ti ti-cloud-data-connection",
		"ti ti-cloud-computing", "ti ti-cloud-check", "ti ti-cloud-up", "ti ti-cloud-down", "ti ti-cloud-lock", "ti ti-cloud-off",
		"ti ti-cloud-x", "ti ti-database", "ti ti-database-export", "ti ti-database-import", "ti ti-database-cog", "ti ti-database-off",
		"ti ti-database-search", "ti ti-database-smile", "ti ti-table", "ti ti-table-options", "ti ti-table-export", "ti ti-table-import",
		"ti ti-network", "ti ti-router", "ti ti-route", "ti ti-world", "ti ti-world-www", "ti ti-access-point",
		"ti ti-wifi", "ti ti-wifi-off", "ti ti-antenna-bars-5", "ti ti-broadcast", "ti ti-radar-2", "ti ti-satellite",
		"ti ti-satellite-off", "ti ti-direction-sign", "ti ti-git-branch", "ti ti-git-commit", "ti ti-git-merge", "ti ti-git-pull-request",
		"ti ti-brand-docker", "ti ti-brand-kubernetes", "ti ti-brand-github", "ti ti-brand-gitlab", "ti ti-brand-openai", "ti ti-brand-aws",
		"ti ti-brand-google", "ti ti-brand-windows", "ti ti-brand-debian", "ti ti-brand-ubuntu", "ti ti-brand-archlinux", "ti ti-brand-python",
		"ti ti-brand-javascript", "ti ti-brand-typescript", "ti ti-brand-go", "ti ti-brand-c-sharp", "ti ti-brand-vscode", "ti ti-brand-powershell",
		"ti ti-device-desktop", "ti ti-device-imac", "ti ti-device-laptop", "ti ti-device-tablet", "ti ti-device-mobile", "ti ti-devices",
		"ti ti-devices-2", "ti ti-screen-share", "ti ti-screen-share-off", "ti ti-app-window", "ti ti-browser", "ti ti-browser-check",
		"ti ti-browser-cog", "ti ti-browser-off", "ti ti-layout-dashboard", "ti ti-layout-grid", "ti ti-layout-list", "ti ti-layout-kanban",
		"ti ti-layout-navbar", "ti ti-layout-sidebar", "ti ti-menu-2", "ti ti-panel-left", "ti ti-panel-right", "ti ti-panorama-horizontal",
		"ti ti-package", "ti ti-package-import", "ti ti-package-export", "ti ti-package-off", "ti ti-box", "ti ti-box-multiple",
		"ti ti-box-model", "ti ti-archive", "ti ti-archive-off", "ti ti-folders", "ti ti-folder", "ti ti-folder-open",
		"ti ti-folder-cog", "ti ti-folder-bolt", "ti ti-file-stack", "ti ti-file-database", "ti ti-file-settings", "ti ti-file-code",
		"ti ti-file-zip", "ti ti-files", "ti ti-scan", "ti ti-search", "ti ti-search-off", "ti ti-filter",
		"ti ti-filter-cog", "ti ti-filter-search", "ti ti-zoom-scan", "ti ti-map-search", "ti ti-binary", "ti ti-braces",
		"ti ti-brackets", "ti ti-code", "ti ti-code-circle", "ti ti-code-dots", "ti ti-code-plus", "ti ti-code-minus",
		"ti ti-api", "ti ti-terminal", "ti ti-terminal-2", "ti ti-command", "ti ti-prompt", "ti ti-script",
		"ti ti-bug", "ti ti-bug-off", "ti ti-test-pipe", "ti ti-progress-check", "ti ti-checks", "ti ti-checkup-list",
		"ti ti-chart-bar", "ti ti-chart-donut", "ti ti-chart-line", "ti ti-chart-area", "ti ti-chart-histogram", "ti ti-chart-infographic",
		"ti ti-activity", "ti ti-activity-heartbeat", "ti ti-pulse", "ti ti-timeline", "ti ti-gauge", "ti ti-meter-cube",
		"ti ti-wave-sine", "ti ti-wave-square", "ti ti-wave-triangle", "ti ti-heart-rate-monitor", "ti ti-clock", "ti ti-clock-cog",
		"ti ti-calendar-time", "ti ti-history", "ti ti-refresh", "ti ti-reload", "ti ti-repeat", "ti ti-rotate-clockwise-2",
		"ti ti-cpu", "ti ti-cpu-2", "ti ti-microchip", "ti ti-memory", "ti ti-ram", "ti ti-gpu-card",
		"ti ti-disc", "ti ti-disc-off", "ti ti-hard-drive", "ti ti-device-sd-card", "ti ti-sd-card", "ti ti-device-usb",
		"ti ti-usb", "ti ti-plug", "ti ti-plug-connected", "ti ti-power", "ti ti-bolt", "ti ti-bolt-off",
		"ti ti-battery", "ti ti-battery-vertical", "ti ti-sun-electricity", "ti ti-circle-dashed", "ti ti-circle-check", "ti ti-circle-x",
		"ti ti-circle-plus", "ti ti-circle-minus", "ti ti-circle-key", "ti ti-key", "ti ti-key-off", "ti ti-keyframe",
		"ti ti-lock", "ti ti-lock-access", "ti ti-lock-check", "ti ti-shield", "ti ti-shield-check", "ti ti-shield-lock",
		"ti ti-shield-half-filled", "ti ti-shield-x", "ti ti-eye-shield", "ti ti-fingerprint", "ti ti-automation", "ti ti-robot",
		"ti ti-settings", "ti ti-settings-2", "ti ti-settings-automation", "ti ti-adjustments", "ti ti-adjustments-alt", "ti ti-adjustments-bolt",
		"ti ti-tool", "ti ti-tools", "ti ti-wrench", "ti ti-hammer", "ti ti-screwdriver", "ti ti-stethoscope",
		"ti ti-tags", "ti ti-tag", "ti ti-tag-starred", "ti ti-pin", "ti ti-pinned", "ti ti-bookmark",
		"ti ti-flag", "ti ti-flag-2", "ti ti-user-cog", "ti ti-user-shield", "ti ti-users-group", "ti ti-building-community",
	}
}

func hasUpdateDay(days, code string) bool {
	for _, item := range strings.Split(strings.ToLower(strings.TrimSpace(days)), ",") {
		if strings.TrimSpace(item) == strings.ToLower(strings.TrimSpace(code)) {
			return true
		}
	}
	return false
}

func updateWindowSummary(start, end, days string) string {
	if strings.TrimSpace(start) == "" || strings.TrimSpace(end) == "" {
		if strings.TrimSpace(days) == "" {
			return "Any time"
		}
		return strings.ToUpper(strings.ReplaceAll(days, ",", " "))
	}
	if strings.TrimSpace(days) == "" {
		return start + " - " + end
	}
	return strings.ToUpper(strings.ReplaceAll(days, ",", " ")) + " · " + start + " - " + end
}

func icon(name string) template.HTML {
	icons := map[string]string{
		"dashboard": `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 13h7V4H4zm9 7h7v-9h-7zM4 20h7v-5H4zm9-9h7V4h-7z"/></svg>`,
		"nodes":     `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="6" rx="2"/><rect x="3" y="14" width="18" height="6" rx="2"/><path d="M7 7h.01M7 17h.01"/></svg>`,
		"groups":    `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M7 7h10M7 12h10M7 17h6"/><rect x="3" y="4" width="18" height="16" rx="2"/></svg>`,
		"jobs":      `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8"/><path d="M12 8v5l3 2"/></svg>`,
		"settings":  `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 3l2.1 2.4 3.1-.4.9 3 2.8 1.4-1.4 2.8 1.4 2.8-2.8 1.4-.9 3-3.1-.4L12 21l-2.1-2.4-3.1.4-.9-3-2.8-1.4 1.4-2.8-1.4-2.8L5.9 8l.9-3 3.1.4z"/><circle cx="12" cy="12" r="3"/></svg>`,
		"logout":    `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><path d="M10 17l5-5-5-5"/><path d="M15 12H3"/></svg>`,
		"server":    `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="4" y="5" width="16" height="5" rx="2"/><rect x="4" y="14" width="16" height="5" rx="2"/><path d="M8 7.5h.01M8 16.5h.01"/></svg>`,
		"ubuntu":    `<svg viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="3"/><circle cx="6" cy="12" r="2"/><circle cx="15" cy="6.5" r="2"/><circle cx="15" cy="17.5" r="2"/><path d="M8 12h2M13 8l-1.5 2M13 16l-1.5-2"/></svg>`,
		"debian":    `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M14 5c2.9 0 5 2 5 5 0 2.1-1.3 4-3.3 4.8.7-.8 1.1-1.9 1.1-3.1 0-2.5-1.7-4.4-4.3-4.4-2.3 0-4 1.7-4 4 0 2.2 1.5 4.1 3.8 5.1 1.2.5 2.6.7 3.7 1.4.9.6 1.4 1.4 1.4 2.3 0 1.8-1.7 3-4.2 3-3.3 0-5.9-2-5.9-4.8 0-1.5.8-2.6 2.2-3.2-.3.5-.4 1-.4 1.6 0 2.2 2 3.8 4.7 3.8 1.9 0 3.1-.7 3.1-1.9 0-.6-.4-1.1-1.2-1.5-1-.5-2.4-.7-3.8-1.3C8.8 16 7 13.9 7 11.2 7 7.7 9.8 5 14 5Z"/></svg>`,
		"arch":      `<svg viewBox="0 0 24 24" fill="currentColor"><path d="m12 3 6.2 14.8c-.9-.5-1.9-.8-2.8-.6-.6.1-1.1.5-1.6 1-.3-.3-.6-.8-.8-1.5-.3-1-.5-2.4-.4-4.2-.4.9-.7 1.9-.8 2.9-.1.9-.1 1.8 0 2.6-.8-.4-1.7-.5-2.6-.2-.6.2-1.2.5-1.7 1.1L12 3Z"/></svg>`,
	}

	if svg, ok := icons[name]; ok {
		return template.HTML(svg)
	}

	return template.HTML(`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="9"/></svg>`)
}
