package views

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

//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,
		"nodeIconName":            nodeIconName,
		"nodeIconPending":         nodeIconPending,
		"packageManagerIconClass": packageManagerIconClass,
		"packageManagerLabel":     packageManagerLabel,
		"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 "fa-brands fa-ubuntu"
	case strings.Contains(value, "debian"):
		return "fa-brands fa-debian"
	case strings.Contains(value, "arch"), strings.Contains(value, "manjaro"), strings.Contains(value, "endeavouros"):
		return "ti ti-brand-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 "fa-brands fa-debian"
	case "pacman":
		return "ti ti-brand-archlinux"
	default:
		return "ti ti-server-2"
	}
}

func nodeIconName(distro, packageManager string) string {
	value := strings.ToLower(strings.TrimSpace(distro))
	switch {
	case strings.Contains(value, "ubuntu"):
		return "ubuntu"
	case strings.Contains(value, "debian"):
		return "debian"
	case strings.Contains(value, "arch"), strings.Contains(value, "manjaro"), strings.Contains(value, "endeavouros"):
		return "arch"
	}

	switch strings.ToLower(strings.TrimSpace(packageManager)) {
	case "apt":
		return "debian"
	case "pacman":
		return "arch"
	default:
		return "server"
	}
}

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 "fa-brands fa-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 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>`)
}
