// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package setting

import (
	"errors"
	"net/http"

	"gitea.dev/models/actions"
	repo_model "gitea.dev/models/repo"
	unit_model "gitea.dev/models/unit"
	user_model "gitea.dev/models/user"
	"gitea.dev/modules/templates"
	"gitea.dev/modules/util"
	shared_actions "gitea.dev/routers/web/shared/actions"
	"gitea.dev/services/context"
	repo_service "gitea.dev/services/repository"
)

const tplRepoActionsGeneralSettings templates.TplName = "repo/settings/actions"

func ActionsGeneralSettings(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("actions.general")
	ctx.Data["PageType"] = "general"
	ctx.Data["PageIsActionsSettingsGeneral"] = true

	actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions)
	if err != nil && !repo_model.IsErrUnitTypeNotExist(err) {
		ctx.ServerError("GetUnit", err)
		return
	}
	if actionsUnit == nil { // no actions unit
		ctx.HTML(http.StatusOK, tplRepoActionsGeneralSettings)
		return
	}

	actionsCfg := actionsUnit.ActionsConfig()

	// Token permission settings
	ctx.Data["TokenPermissionModePermissive"] = repo_model.ActionsTokenPermissionModePermissive
	ctx.Data["TokenPermissionModeRestricted"] = repo_model.ActionsTokenPermissionModeRestricted

	// Follow owner config (only for repos in orgs)
	ctx.Data["OverrideOwnerConfig"] = actionsCfg.OverrideOwnerConfig
	if actionsCfg.OverrideOwnerConfig {
		ctx.Data["MaxTokenPermissions"] = actionsCfg.GetMaxTokenPermissions()
		ctx.Data["TokenPermissionMode"] = actionsCfg.TokenPermissionMode
		ctx.Data["EnableMaxTokenPermissions"] = actionsCfg.MaxTokenPermissions != nil
	} else {
		ownerActionsConfig, err := actions.GetOwnerActionsConfig(ctx, ctx.Repo.Repository.OwnerID)
		if err != nil {
			ctx.ServerError("GetOwnerActionsConfig", err)
			return
		}
		ctx.Data["MaxTokenPermissions"] = ownerActionsConfig.GetMaxTokenPermissions()
		ctx.Data["TokenPermissionMode"] = ownerActionsConfig.TokenPermissionMode
		ctx.Data["EnableMaxTokenPermissions"] = ownerActionsConfig.MaxTokenPermissions != nil
	}

	if ctx.Repo.Repository.IsPrivate {
		collaborativeOwnerIDs := actionsCfg.CollaborativeOwnerIDs
		collaborativeOwners, err := user_model.GetUsersByIDs(ctx, collaborativeOwnerIDs)
		if err != nil {
			ctx.ServerError("GetUsersByIDs", err)
			return
		}
		ctx.Data["CollaborativeOwners"] = collaborativeOwners
	}

	ctx.HTML(http.StatusOK, tplRepoActionsGeneralSettings)
}

func ActionsUnitPost(ctx *context.Context) {
	redirectURL := ctx.Repo.RepoLink + "/settings/actions/general"
	enableActionsUnit := ctx.FormBool("enable_actions")
	repo := ctx.Repo.Repository

	var err error
	if enableActionsUnit && !unit_model.TypeActions.UnitGlobalDisabled() {
		err = repo_service.UpdateRepositoryUnits(ctx, repo, []repo_model.RepoUnit{newRepoUnit(repo, unit_model.TypeActions, nil)}, nil)
	} else if !unit_model.TypeActions.UnitGlobalDisabled() {
		err = repo_service.UpdateRepositoryUnits(ctx, repo, nil, []unit_model.Type{unit_model.TypeActions})
	}

	if err != nil {
		ctx.ServerError("UpdateRepositoryUnits", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
	ctx.Redirect(redirectURL)
}

func AddCollaborativeOwner(ctx *context.Context) {
	collUser, err := user_model.GetUserByName(ctx, ctx.FormString("collaborative_owner"))
	if err != nil {
		if errors.Is(err, util.ErrNotExist) {
			ctx.JSONError(ctx.Tr("form.user_not_exist"))
		} else {
			ctx.ServerError("GetUserByName", err)
		}
		return
	}

	actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions)
	if err != nil {
		ctx.ServerError("GetUnit", err)
		return
	}
	actionsCfg := actionsUnit.ActionsConfig()
	actionsCfg.AddCollaborativeOwner(collUser.ID)
	if err := repo_model.UpdateRepoUnitConfig(ctx, actionsUnit); err != nil {
		ctx.ServerError("UpdateRepoUnitConfig", err)
		return
	}

	ctx.JSONOK()
}

func DeleteCollaborativeOwner(ctx *context.Context) {
	ownerID := ctx.FormInt64("id")

	actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions)
	if err != nil {
		ctx.ServerError("GetUnit", err)
		return
	}
	actionsCfg := actionsUnit.ActionsConfig()
	if !actionsCfg.IsCollaborativeOwner(ownerID) {
		ctx.Flash.Error(ctx.Tr("actions.general.collaborative_owner_not_exist"))
		ctx.JSONErrorNotFound()
		return
	}
	actionsCfg.RemoveCollaborativeOwner(ownerID)
	if err := repo_model.UpdateRepoUnitConfig(ctx, actionsUnit); err != nil {
		ctx.ServerError("UpdateRepoUnitConfig", err)
		return
	}

	ctx.JSONOK()
}

// UpdateTokenPermissions updates the token permission settings for the repository
func UpdateTokenPermissions(ctx *context.Context) {
	redirectURL := ctx.Repo.RepoLink + "/settings/actions/general"

	actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions)
	if err != nil {
		ctx.ServerError("GetUnit", err)
		return
	}

	actionsCfg := actionsUnit.ActionsConfig()

	// Update Override Owner Config (for repos in orgs)
	// If checked, it means we WANT to override (opt-out of following)
	actionsCfg.OverrideOwnerConfig = ctx.FormBool("override_owner_config")

	// Update permission mode (only if overriding owner config)
	shouldUpdate := actionsCfg.OverrideOwnerConfig

	if shouldUpdate {
		permissionMode, permissionModeValid := util.EnumValue(repo_model.ActionsTokenPermissionMode(ctx.FormString("token_permission_mode")))
		if !permissionModeValid {
			ctx.Flash.Error("Invalid token permission mode")
			ctx.Redirect(redirectURL)
			return
		}
		actionsCfg.TokenPermissionMode = permissionMode
	}

	// Update Maximum Permissions (radio buttons: none/read/write)
	enableMaxPermissions := ctx.FormBool("enable_max_permissions")
	if shouldUpdate {
		if enableMaxPermissions {
			actionsCfg.MaxTokenPermissions = shared_actions.ParseMaxTokenPermissions(ctx)
		} else {
			// If not enabled, ensure any sent permissions are ignored and set to nil
			actionsCfg.MaxTokenPermissions = nil
		}
	}

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

	ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
	ctx.Redirect(redirectURL)
}
