// Package templates provides embedded HTML templates for the RouteWatch application
package templates

import (
	_ "embed"
	"html/template"
	"net/url"
	"strings"
	"sync"
	"time"

	"git.eeqj.de/sneak/routewatch/internal/version"
)

//go:embed index.html
var indexHTML string

//go:embed status.html
var statusHTML string

//go:embed as_detail.html
var asDetailHTML string

//go:embed prefix_detail.html
var prefixDetailHTML string

//go:embed prefix_length.html
var prefixLengthHTML string

// Templates contains all parsed templates
type Templates struct {
	// Index is the template for the home page
	Index *template.Template
	// Status is the template for the main status page
	Status *template.Template
	// ASDetail is the template for displaying AS (Autonomous System) details
	ASDetail *template.Template
	// PrefixDetail is the template for displaying prefix details
	PrefixDetail *template.Template
	// PrefixLength is the template for displaying prefixes by length
	PrefixLength *template.Template
}

var (
	//nolint:gochecknoglobals // Singleton pattern for templates
	defaultTemplates *Templates
	//nolint:gochecknoglobals // Singleton pattern for templates
	once sync.Once
)

const (
	hoursPerDay   = 24
	daysPerMonth  = 30
	cidrPartCount = 2 // A CIDR has two parts: prefix and length
)

// timeSince returns a human-readable duration since the given time
func timeSince(t time.Time) string {
	duration := time.Since(t)
	if duration < time.Minute {
		return "just now"
	}
	if duration < time.Hour {
		minutes := int(duration.Minutes())
		if minutes == 1 {
			return "1 minute ago"
		}

		return duration.Truncate(time.Minute).String() + " ago"
	}
	if duration < hoursPerDay*time.Hour {
		hours := int(duration.Hours())
		if hours == 1 {
			return "1 hour ago"
		}

		return duration.Truncate(time.Hour).String() + " ago"
	}
	days := int(duration.Hours() / hoursPerDay)
	if days == 1 {
		return "1 day ago"
	}
	if days < daysPerMonth {
		return duration.Truncate(hoursPerDay*time.Hour).String() + " ago"
	}

	return t.Format("2006-01-02")
}

// prefixURL generates a URL path for a prefix in CIDR notation.
// Takes a prefix like "192.168.1.0/24" and returns "/prefix/192.168.1.0/24"
// with the prefix part URL-encoded to handle IPv6 colons.
func prefixURL(cidr string) string {
	// Split CIDR into prefix and length
	parts := strings.SplitN(cidr, "/", cidrPartCount)
	if len(parts) != cidrPartCount {
		// Fallback if no slash found
		return "/prefix/" + url.PathEscape(cidr) + "/0"
	}

	return "/prefix/" + url.PathEscape(parts[0]) + "/" + parts[1]
}

// initTemplates parses all embedded templates
func initTemplates() {
	var err error

	defaultTemplates = &Templates{}

	// Create common template functions
	funcs := template.FuncMap{
		"timeSince":       timeSince,
		"urlEncode":       url.QueryEscape,
		"prefixURL":       prefixURL,
		"appName":         func() string { return version.Name },
		"appAuthor":       func() string { return version.Author },
		"appAuthorURL":    func() string { return version.AuthorURL },
		"appLicense":      func() string { return version.License },
		"appRepoURL":      func() string { return version.RepoURL },
		"appGitRevision":  func() string { return version.GitRevisionShort },
		"appGitCommitURL": func() string { return version.CommitURL() },
	}

	// Parse index template
	defaultTemplates.Index, err = template.New("index").Funcs(funcs).Parse(indexHTML)
	if err != nil {
		panic("failed to parse index template: " + err.Error())
	}

	// Parse status template
	defaultTemplates.Status, err = template.New("status").Funcs(funcs).Parse(statusHTML)
	if err != nil {
		panic("failed to parse status template: " + err.Error())
	}

	// Parse AS detail template
	defaultTemplates.ASDetail, err = template.New("asDetail").Funcs(funcs).Parse(asDetailHTML)
	if err != nil {
		panic("failed to parse AS detail template: " + err.Error())
	}

	// Parse prefix detail template
	defaultTemplates.PrefixDetail, err = template.New("prefixDetail").Funcs(funcs).Parse(prefixDetailHTML)
	if err != nil {
		panic("failed to parse prefix detail template: " + err.Error())
	}

	// Parse prefix length template
	defaultTemplates.PrefixLength, err = template.New("prefixLength").Funcs(funcs).Parse(prefixLengthHTML)
	if err != nil {
		panic("failed to parse prefix length template: " + err.Error())
	}
}

// Get returns the singleton Templates instance
func Get() *Templates {
	once.Do(initTemplates)

	return defaultTemplates
}

// IndexTemplate returns the parsed index template
func IndexTemplate() *template.Template {
	return Get().Index
}

// StatusTemplate returns the parsed status template
func StatusTemplate() *template.Template {
	return Get().Status
}

// ASDetailTemplate returns the parsed AS detail template
func ASDetailTemplate() *template.Template {
	return Get().ASDetail
}

// PrefixDetailTemplate returns the parsed prefix detail template
func PrefixDetailTemplate() *template.Template {
	return Get().PrefixDetail
}

// PrefixLengthTemplate returns the parsed prefix length template
func PrefixLengthTemplate() *template.Template {
	return Get().PrefixLength
}
