package main

import (
	"net/http"
	"strings"
	"time"
)

type UserSettings struct {
	Theme          string
	SiteLanguage   string
	SearchLanguage string
	SafeSearch     string
	IsThemeDark    bool
}

func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings {
	var settings UserSettings
	saveRequired := false

	for _, cd := range AllCookies {
		// Attempt to read the cookie
		if cookie, err := r.Cookie(cd.Name); err == nil {
			// Use SetValue to update the correct UserSettings field
			cd.SetValue(&settings, cookie.Value)
		} else {
			// If cookie is missing and you want a default value, set it here
			switch cd.Name {
			case "theme":
				// Default theme to "dark" if missing
				cd.SetValue(&settings, "dark")
				saveRequired = true
			case "site_language":
				// Fallback to Accept-Language or "en"
				acceptLang := r.Header.Get("Accept-Language")
				if acceptLang != "" {
					cd.SetValue(&settings, normalizeLangCode(acceptLang))
				} else {
					cd.SetValue(&settings, "en")
				}
				saveRequired = true
			case "safe":
				// Default safe to ""
				cd.SetValue(&settings, "")
				saveRequired = true
				// etc. for other cookies if needed
			}
		}
	}

	// If theme was set, update IsThemeDark just to be sure
	// Alternatively do it inside SetValue for "theme"
	settings.IsThemeDark = settings.Theme == "dark" ||
		settings.Theme == "night" ||
		settings.Theme == "black" ||
		settings.Theme == "latte"

	// Save any new default cookies that might have been triggered
	if saveRequired {
		saveUserSettings(w, settings)
	}

	return settings
}

func saveUserSettings(w http.ResponseWriter, settings UserSettings) {
	expiration := time.Now().Add(90 * 24 * time.Hour)

	for _, cd := range AllCookies {
		http.SetCookie(w, &http.Cookie{
			Name:     cd.Name,
			Value:    cd.GetValue(settings),
			Path:     "/",
			Expires:  expiration,
			Secure:   true, // Ensure HTTPS is required
			HttpOnly: true,
			SameSite: http.SameSiteStrictMode, // Restrict cross-site usage
		})
	}

	printDebug("settings saved: %v", settings)
}

func handleSaveSettings(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		// Load current settings
		settings := loadUserSettings(w, r)

		// Update only the settings that were submitted in the form
		if theme := r.FormValue("theme"); theme != "" {
			settings.Theme = theme
		}

		// Update site language if provided
		if siteLang := r.FormValue("site_lang"); siteLang != "" {
			settings.SiteLanguage = siteLang
		} else {
			// If site_lang is empty, try to get from Accept-Language header
			acceptLang := r.Header.Get("Accept-Language")
			if acceptLang != "" {
				settings.SiteLanguage = strings.Split(acceptLang, ",")[0]
			}
		}

		// Update search language if provided
		if searchLang := r.FormValue("search_lang"); searchLang != "" {
			settings.SearchLanguage = searchLang
		}

		// Update safe search if provided
		if safe := r.FormValue("safe"); safe != "" {
			settings.SafeSearch = safe
		}

		// Save the updated settings
		saveUserSettings(w, settings)

		// Redirect back to the previous page or settings page
		http.Redirect(w, r, r.FormValue("past"), http.StatusSeeOther)
	}
}

func handleSettings(w http.ResponseWriter, r *http.Request) {
	// Load user settings
	settings := loadUserSettings(w, r)

	// Prepare data with user settings and icon paths as a map
	data := map[string]interface{}{
		"LanguageOptions":   languageOptions,
		"CurrentSiteLang":   settings.SiteLanguage,
		"CurrentSearchLang": settings.SearchLanguage,
		"Theme":             settings.Theme,
		"Safe":              settings.SafeSearch,
		"IsThemeDark":       settings.IsThemeDark,
	}

	printDebug("Rendering settings with data: %+v", data)

	// Use renderTemplate to include the icons
	renderTemplate(w, "settings.html", data)
}

// Helper function to normalize language codes
func normalizeLangCode(lang string) string {
	lang = strings.ToLower(lang)

	// First, check if the language code is already valid
	if isValidLangCode(lang) {
		return lang
	}

	// Strip regional codes (e.g., en-US -> en)
	if strings.Contains(lang, "-") {
		lang = strings.Split(lang, "-")[0]
	}

	// Re-check if the normalized version is valid
	if isValidLangCode(lang) {
		return lang
	}

	// If the language is not recognized, default to "en"
	return "en"
}

// Helper function to check if a language code exists in the language options
func isValidLangCode(lang string) bool {
	for _, opt := range languageOptions {
		if opt.Code == lang {
			return true
		}
	}
	return false
}

// CookieDefinition describes how a single cookie is handled
type CookieDefinition struct {
	Name string
	// GetValue extracts the corresponding field from UserSettings
	GetValue func(UserSettings) string
	// SetValue updates the corresponding field in UserSettings
	SetValue func(*UserSettings, string)
	// Description used in privacy table or docs
	Description string
}

// AllCookies defines every cookie we handle in a single slice.
// Add or remove entries here, and the rest updates automatically.
var AllCookies = []CookieDefinition{
	{
		Name:        "theme",
		Description: "Stores the selected theme (dark, light, etc.)",
		GetValue: func(s UserSettings) string {
			return s.Theme
		},
		SetValue: func(s *UserSettings, val string) {
			s.Theme = val
			s.IsThemeDark = (val == "dark" || val == "night" || val == "black" || val == "latte")
		},
	},
	{
		Name:        "site_language",
		Description: "Stores the preferred site language.",
		GetValue: func(s UserSettings) string {
			return s.SiteLanguage
		},
		SetValue: func(s *UserSettings, val string) {
			s.SiteLanguage = val
		},
	},
	{
		Name:        "search_language",
		Description: "Stores the preferred language for search results.",
		GetValue: func(s UserSettings) string {
			return s.SearchLanguage
		},
		SetValue: func(s *UserSettings, val string) {
			s.SearchLanguage = val
		},
	},
	{
		Name:        "safe",
		Description: "Stores the Safe Search setting.",
		GetValue: func(s UserSettings) string {
			return s.SafeSearch
		},
		SetValue: func(s *UserSettings, val string) {
			s.SafeSearch = val
		},
	},
}

type CookieRow struct {
	Name        string
	Value       string
	Description string
	Expiration  string
}

func generateCookieTable(r *http.Request) []CookieRow {
	var rows []CookieRow
	for _, cd := range AllCookies {
		value := "[Not Set]"
		if cookie, err := r.Cookie(cd.Name); err == nil {
			value = cookie.Value
		}
		rows = append(rows, CookieRow{
			Name:        cd.Name,
			Value:       value,
			Description: cd.Description,
			Expiration:  "90 days",
		})
	}
	return rows
}