package web

import (
	"net/http"
	"net/url"
	"sort"
	"strings"

	"github.com/vaughan0/go-ini"
	"sourcecraft.dev/bigbes/sr-ht-core/config"

	"sourcecraft.dev/bigbes/sr-ht-compare/authz"
)

// navCanonical is the SourceHut service-switcher order. Services not listed here
// (including our own compare) sort alphabetically after these.
var navCanonical = []string{"hub", "git", "hg", "lists", "todo", "builds", "man", "meta"}

// navExcluded are service sections that never appear in the switcher: paste and
// pages have no top-level UI worth linking, and hub is rendered as the brand.
var navExcluded = map[string]bool{"paste": true, "pages": true, "hub": true}

// navItem is one entry in the service switcher.
type navItem struct {
	Name   string // short service name, e.g. "git"
	Origin string // external origin URL
	Active bool   // true for compare.sr.ht (this service)
}

// buildNav derives the service switcher from the shared config: every section
// whose name ends in ".sr.ht" (with a configured origin) except paste/pages/hub,
// ordered by navCanonical then alphabetically, with compare.sr.ht marked active.
func buildNav(conf ini.File) []navItem {
	var items []navItem
	for section := range conf {
		if !strings.HasSuffix(section, ".sr.ht") {
			continue
		}
		short := strings.TrimSuffix(section, ".sr.ht")
		if navExcluded[short] {
			continue
		}
		origin := config.GetOrigin(conf, section, true)
		if origin == "" {
			continue
		}
		items = append(items, navItem{
			Name:   short,
			Origin: origin,
			Active: section == "compare.sr.ht",
		})
	}
	sort.SliceStable(items, func(i, j int) bool {
		ci, cj := canonIndex(items[i].Name), canonIndex(items[j].Name)
		if ci != cj {
			return ci < cj
		}
		return items[i].Name < items[j].Name
	})
	return items
}

// canonIndex returns a service's position in navCanonical, or a sentinel past
// the end for services that are not canonically ordered.
func canonIndex(name string) int {
	for i, n := range navCanonical {
		if n == name {
			return i
		}
	}
	return len(navCanonical)
}

// viewData is the root value every template is executed against: the chrome
// fields are common to all pages; Data carries the page-specific payload.
type viewData struct {
	Title       string
	SiteName    string
	HubOrigin   string // non-empty ⇒ brand links to hub instead of "/"
	Nav         []navItem
	Username    string // "" for an anonymous viewer
	LoginURL    string
	LogoutURL   string
	RegisterURL string
	ProfileURL  string
	CSSHref     string
	BundleHref  string
	Environment string
	ShowBanner  bool

	// ContainerClass selects the width of the page's content wrapper: the
	// centered Bootstrap "container" by default, or "container-fluid" for the
	// full-bleed diff views (commit / compare).
	ContainerClass string

	Data any
}

// chrome builds the common chrome fields for a request. Login return_to is the
// current full URL (so the viewer lands back where they were); logout return_to
// is this service's origin.
func (s *Server) chrome(r *http.Request) viewData {
	username := authz.ForContext(r.Context())

	current := s.compareOrigin + r.URL.RequestURI()
	loginURL := s.metaOrigin + "/login?return_to=" + url.QueryEscape(current)
	logoutURL := s.metaOrigin + "/logout?return_to=" + url.QueryEscape(s.compareOrigin)

	profileURL := s.metaOrigin + "/profile"
	if s.hubOrigin != "" && username != "" {
		profileURL = s.hubOrigin + "/~" + username
	}

	return viewData{
		ContainerClass: "container",
		SiteName:       s.siteName,
		HubOrigin:      s.hubOrigin,
		Nav:            s.nav,
		Username:       username,
		LoginURL:       loginURL,
		LogoutURL:      logoutURL,
		RegisterURL:    s.metaOrigin,
		ProfileURL:     profileURL,
		CSSHref:        s.cssHref,
		BundleHref:     s.bundleHref,
		Environment:    strings.ToUpper(s.environment),
		ShowBanner:     s.environment != "" && s.environment != "production",
	}
}
