package server

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	"heckel.io/ntfy/v2/util"
)

// handleWebApp serves the embedded web app's index for client-side (SPA) routes that the
// browser router resolves, so the app shell loads and the client-side router takes over.
func (s *Server) handleWebApp(w http.ResponseWriter, r *http.Request, v *visitor) error {
	r.URL.Path = webAppIndex
	return s.handleStatic(w, r, v)
}

// handleWebAppNoIndex serves the web app index for the magic-link landing pages, whose path
// carries a one-time token. The response is marked no-referrer (so the token can't leak to third
// parties via the Referer header) and noindex (so it never gets indexed).
func (s *Server) handleWebAppNoIndex(w http.ResponseWriter, r *http.Request, v *visitor) error {
	w.Header().Set("Referrer-Policy", "no-referrer")
	w.Header().Set("X-Robots-Tag", "noindex")
	return s.handleWebApp(w, r, v)
}

func (s *Server) handleConfig(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
	w.Header().Set("Cache-Control", "no-cache")
	return s.writeJSON(w, s.configResponse())
}

func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
	b, err := json.MarshalIndent(s.configResponse(), "", "  ")
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "text/javascript")
	w.Header().Set("Cache-Control", "no-cache")
	_, err = io.WriteString(w, fmt.Sprintf("// Generated server configuration\nvar config = %s;\n", string(b)))
	return err
}

// handleWebManifest serves the web app manifest for the progressive web app (PWA)
func (s *Server) handleWebManifest(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
	response := &webManifestResponse{
		Name:            "ntfy",
		Description:     "ntfy lets you send push notifications via scripts from any computer or phone",
		ShortName:       "ntfy",
		Scope:           "/",
		StartURL:        s.config.WebRoot,
		Display:         "standalone",
		BackgroundColor: "#ffffff",
		ThemeColor:      "#317f6f",
		Icons: []*webManifestIcon{
			{SRC: "/static/images/pwa-192x192.png", Sizes: "192x192", Type: "image/png"},
			{SRC: "/static/images/pwa-512x512.png", Sizes: "512x512", Type: "image/png"},
		},
	}
	return s.writeJSONWithContentType(w, response, "application/manifest+json")
}

// handleStatic returns all static resources (excluding the docs), including the web app
func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request, _ *visitor) error {
	r.URL.Path = webSiteDir + r.URL.Path
	util.Gzip(http.FileServer(http.FS(webFsCached))).ServeHTTP(w, r)
	return nil
}

// handleDocs returns static resources related to the docs
func (s *Server) handleDocs(w http.ResponseWriter, r *http.Request, _ *visitor) error {
	util.Gzip(http.FileServer(http.FS(docsStaticCached))).ServeHTTP(w, r)
	return nil
}

func (s *Server) configResponse() *apiConfigResponse {
	return &apiConfigResponse{
		BaseURL:             "", // Will translate to window.location.origin
		AppRoot:             s.config.WebRoot,
		EnableLogin:         s.config.EnableLogin,
		RequireLogin:        s.config.RequireLogin,
		EnableSignup:        s.config.EnableSignup,
		EnablePayments:      s.config.StripeSecretKey != "",
		EnableCalls:         s.config.TwilioAccount != "",
		EnableEmails:        s.config.SMTPSenderFrom != "",
		EnableResetPassword: s.config.SMTPSenderFrom != "" && s.config.BaseURL != "", // Reset links need SMTP + an absolute base-url
		EnableReservations:  s.config.EnableReservations,
		EnableWebPush:       s.config.WebPushPublicKey != "",
		BillingContact:      s.config.BillingContact,
		WebPushPublicKey:    s.config.WebPushPublicKey,
		DisallowedTopics:    s.config.DisallowedTopics,
		ConfigHash:          s.config.Hash(),
	}
}
