// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later

package admin

import (
	"io"
	"net/http"
	"os"
	"path/filepath"

	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting"
	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/templates"
	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)

const tplBranding templates.TplName = "admin/branding"

// brandingImageDir returns the path to the custom branding images directory.
func brandingImageDir() string {
	return filepath.Join(setting.CustomPath, "public", "assets", "img")
}

// Branding shows the admin branding page.
func Branding(ctx *context.Context) {
	ctx.Data["Title"] = "Branding"
	ctx.Data["PageIsAdminBranding"] = true

	imgDir := brandingImageDir()
	ctx.Data["HasNavIcon"] = fileExists(filepath.Join(imgDir, "logo-small.png"))
	ctx.Data["HasLoginLogo"] = fileExists(filepath.Join(imgDir, "login-logo.png"))
	ctx.Data["HasFavicon"] = fileExists(filepath.Join(imgDir, "favicon.png"))

	ctx.Data["MetaDescription"] = setting.UI.Meta.Description
	ctx.Data["MetaAuthor"] = setting.UI.Meta.Author
	ctx.Data["HelpURL"] = setting.HelpURL
	ctx.Data["SupportURL"] = setting.SupportURL

	ctx.HTML(http.StatusOK, tplBranding)
}

// BrandingSettings handles the text branding form submission.
func BrandingSettings(ctx *context.Context) {
	appName := ctx.FormString("app_name")
	description := ctx.FormString("description")
	helpURL := ctx.FormString("help_url")
	supportURL := ctx.FormString("support_url")
	author := ctx.FormString("author")

	// Update in-memory settings
	if appName != "" {
		setting.AppName = appName
	}
	if description != "" {
		setting.UI.Meta.Description = description
	}
	setting.HelpURL = helpURL
	setting.SupportURL = supportURL
	if author != "" {
		setting.UI.Meta.Author = author
	}

	// Persist to app.ini
	cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
	if err != nil {
		ctx.Flash.Error("Failed to load config: " + err.Error())
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}

	if appName != "" {
		cfg.Section("").Key("APP_NAME").SetValue(appName)
	}
	if description != "" {
		cfg.Section("ui.meta").Key("DESCRIPTION").SetValue(description)
	}
	cfg.Section("").Key("HELP_URL").SetValue(helpURL)
	cfg.Section("").Key("SUPPORT_URL").SetValue(supportURL)
	if author != "" {
		cfg.Section("ui.meta").Key("AUTHOR").SetValue(author)
	}

	if err := cfg.SaveTo(setting.CustomConf); err != nil {
		ctx.Flash.Error("Failed to save config: " + err.Error())
		log.Error("SaveTo %s: %v", setting.CustomConf, err)
	} else {
		ctx.Flash.Success("Branding settings saved")
		log.Info("Branding settings updated: AppName=%s, Author=%s", appName, author)
	}

	ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
}

// BrandingUpload handles branding image uploads.
func BrandingUpload(ctx *context.Context) {
	imageType := ctx.FormString("type")
	if imageType == "" {
		ctx.Flash.Error("No image type specified")
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}

	var filename string
	switch imageType {
	case "nav-icon":
		filename = "logo-small.png"
	case "login-logo":
		filename = "login-logo.png"
	case "favicon":
		filename = "favicon.png"
	default:
		ctx.Flash.Error("Invalid image type: " + imageType)
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}

	file, header, err := ctx.Req.FormFile("file")
	if err != nil {
		ctx.Flash.Error("Upload failed: " + err.Error())
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}
	defer file.Close()

	if header.Size > 2*1024*1024 {
		ctx.Flash.Error("File too large (max 2MB)")
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}

	imgDir := brandingImageDir()
	if err := os.MkdirAll(imgDir, 0o755); err != nil {
		ctx.Flash.Error("Failed to create image directory")
		log.Error("MkdirAll %s: %v", imgDir, err)
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}

	destPath := filepath.Join(imgDir, filename)
	dest, err := os.Create(destPath)
	if err != nil {
		ctx.Flash.Error("Failed to save image")
		log.Error("Create %s: %v", destPath, err)
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}
	defer dest.Close()

	if _, err := io.Copy(dest, file); err != nil {
		ctx.Flash.Error("Failed to write image")
		log.Error("Copy to %s: %v", destPath, err)
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}

	// Remove SVG override if present
	svgPath := filepath.Join(imgDir, filename[:len(filename)-4]+".svg")
	if fileExists(svgPath) {
		os.Remove(svgPath)
	}

	ctx.Flash.Success("Branding image updated: " + imageType)
	log.Info("Branding image uploaded: %s (%d bytes)", filename, header.Size)
	ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
}

// BrandingReset removes a custom branding image, reverting to the built-in default.
func BrandingReset(ctx *context.Context) {
	imageType := ctx.FormString("type")

	var filename string
	switch imageType {
	case "nav-icon":
		filename = "logo-small.png"
	case "login-logo":
		filename = "login-logo.png"
	case "favicon":
		filename = "favicon.png"
	default:
		ctx.Flash.Error("Invalid image type")
		ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
		return
	}

	path := filepath.Join(brandingImageDir(), filename)
	if fileExists(path) {
		if err := os.Remove(path); err != nil {
			ctx.Flash.Error("Failed to remove custom image")
			log.Error("Remove %s: %v", path, err)
		} else {
			ctx.Flash.Success("Reset to default: " + imageType)
			log.Info("Branding reset to default: %s", filename)
		}
	} else {
		ctx.Flash.Info("Already using default: " + imageType)
	}

	ctx.Redirect(setting.AppSubURL + "/-/admin/branding")
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}
