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

package setting

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

	issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
	repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/repo"
	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/templates"
	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)

const tplSettingsMetadata templates.TplName = "repo/settings/metadata"

// Metadata displays the consolidated metadata page:
// project identity (manifest), update server config, and repo-scoped custom fields.
func Metadata(ctx *context.Context) {
	ctx.Data["Title"] = "Metadata"
	ctx.Data["PageIsSettingsMetadata"] = true

	repoID := ctx.Repo.Repository.ID
	ownerID := ctx.Repo.Repository.OwnerID

	// Load manifest (project identity).
	manifest, err := repo_model.GetRepoMetadata(ctx, repoID)
	if err != nil {
		ctx.ServerError("GetRepoMetadata", err)
		return
	}
	if manifest == nil {
		manifest = &repo_model.RepoMetadata{
			RepoID:      repoID,
			Name:        ctx.Repo.Repository.Name,
			Org:         ctx.Repo.Repository.OwnerName,
			Description: ctx.Repo.Repository.Description,
		}
	}
	ctx.Data["Manifest"] = manifest

	// Load repo-scoped custom fields.
	fields, _ := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeRepo)
	ctx.Data["CustomFieldDefs"] = fields

	values := make(map[int64]string)
	fieldOptions := make(map[int64][]string)
	if len(fields) > 0 {
		values, _ = issues_model.GetCustomFieldValuesMap(ctx, repoID)
		for _, f := range fields {
			if f.Options != "" {
				var opts []string
				if err := json.Unmarshal([]byte(f.Options), &opts); err == nil {
					fieldOptions[f.ID] = opts
				}
			}
		}
	}
	ctx.Data["CustomFieldValues"] = values
	ctx.Data["CustomFieldOptions"] = fieldOptions

	ctx.HTML(http.StatusOK, tplSettingsMetadata)
}

// MetadataPost routes to the correct sub-handler based on the action param.
func MetadataPost(ctx *context.Context) {
	switch ctx.FormString("action") {
	case "manifest":
		saveMetadata(ctx)
	case "customfields":
		saveCustomFields(ctx)
	default:
		saveMetadata(ctx)
	}
}

// spdxToName maps SPDX identifiers to human-readable license names.
var spdxToName = map[string]string{
	"GPL-3.0-or-later":  "GNU General Public License v3 or later",
	"GPL-2.0-or-later":  "GNU General Public License v2 or later",
	"MIT":               "MIT License",
	"Apache-2.0":        "Apache License 2.0",
	"BSD-3-Clause":      "BSD 3-Clause License",
	"BSD-2-Clause":      "BSD 2-Clause License",
	"LGPL-3.0-or-later": "GNU Lesser General Public License v3 or later",
	"MPL-2.0":           "Mozilla Public License 2.0",
	"ISC":               "ISC License",
	"AGPL-3.0-or-later": "GNU Affero General Public License v3 or later",
	"Unlicense":         "The Unlicense",
	"proprietary":       "Proprietary",
}

func saveMetadata(ctx *context.Context) {
	spdx := ctx.FormString("license_spdx")
	licenseName := spdxToName[spdx]

	// Preserve existing values for fields removed from the UI.
	existing, _ := repo_model.GetRepoMetadata(ctx, ctx.Repo.Repository.ID)

	manifest := &repo_model.RepoMetadata{
		RepoID:        ctx.Repo.Repository.ID,
		Name:          ctx.FormString("name"),
		Org:           ctx.FormString("org"),
		Description:   ctx.Repo.Repository.Description,
		LicenseSPDX:   spdx,
		LicenseName:   licenseName,
		VersionPrefix: ctx.FormString("version_prefix"),
		Platform:      ctx.FormString("platform"),
		InfoURL:       ctx.FormString("info_url"),
		TargetVersion: ctx.FormString("target_version"),
		PHPMinimum:    ctx.FormString("php_minimum"),
		ExtensionType:   ctx.FormString("extension_type"),
		EntryPoint:    ctx.FormString("entry_point"),
	}

	// Preserve fields not in the UI but still in the model.
	if existing != nil {
		manifest.ElementName = existing.ElementName
		manifest.StandardsVersion = existing.StandardsVersion
		manifest.StandardsSource = existing.StandardsSource
		manifest.Maintainer = existing.Maintainer
		manifest.MaintainerURL = existing.MaintainerURL
		manifest.Language = existing.Language
	}

	if err := repo_model.CreateOrUpdateRepoMetadata(ctx, manifest); err != nil {
		ctx.ServerError("CreateOrUpdateRepoMetadata", err)
		return
	}

	ctx.Flash.Success("Project identity saved")
	ctx.Redirect(ctx.Repo.RepoLink + "/settings/metadata")
}

func saveCustomFields(ctx *context.Context) {
	repoID := ctx.Repo.Repository.ID
	ownerID := ctx.Repo.Repository.OwnerID

	fields, _ := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeRepo)
	for _, f := range fields {
		val := ctx.Req.FormValue(fmt.Sprintf("field_%d", f.ID))
		if err := issues_model.SetCustomFieldValue(ctx, repoID, f.ID, val); err != nil {
			ctx.ServerError("SetCustomFieldValue", err)
			return
		}
	}

	ctx.Flash.Success("Custom fields saved")
	ctx.Redirect(ctx.Repo.RepoLink + "/settings/metadata")
}

