package org

import (
	"net/http"
	"strings"

	"code.gitea.io/gitea/models/db"
	org_model "code.gitea.io/gitea/models/organization"
	repo_model "code.gitea.io/gitea/models/repo"

	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/convert"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/modules/web"
	container_service "code.gitea.io/gitea/services/packages/container"
	repo_service "code.gitea.io/gitea/services/repository"
	hat_convert "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/convert"
	hat_api "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/structs"
)

func Create(ctx *context.APIContext) {
	form := web.GetForm(ctx).(*api.CreateOrgOption)
	if !ctx.Doer.CanCreateOrganization() {
		ctx.Error(http.StatusForbidden, "create organization not allowed", nil)
		return
	}
	visibility := api.VisibleTypePublic
	if form.Visibility != "" {
		visibility = api.VisibilityModes[form.Visibility]
	}

	org := &org_model.Organization{
		Name:                      form.UserName,
		FullName:                  form.FullName,
		Description:               form.Description,
		Website:                   form.Website,
		Location:                  form.Location,
		IsActive:                  true,
		Type:                      user_model.UserTypeOrganization,
		Visibility:                visibility,
		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
	}

	if err := org_model.CreateOrganization(org, ctx.Doer); err != nil {
		if user_model.IsErrUserAlreadyExist(err) ||
			db.IsErrNameReserved(err) ||
			db.IsErrNameCharsNotAllowed(err) ||
			db.IsErrNamePatternNotAllowed(err) {
			ctx.Error(http.StatusUnprocessableEntity, "", err)
		} else {
			ctx.Error(http.StatusInternalServerError, "CreateOrganization", err)
		}
		return
	}

	team, err := org_model.GetTeam(ctx, org.ID, "")
	if err != nil {
		if user_model.IsErrUserNotExist(err) {
			ctx.NotFound()
		} else {
			ctx.Error(http.StatusInternalServerError, "GetTeam", err)
		}
		return
	}

	if apiOrg, err := hat_convert.ToOrganization(org, team); err != nil {
		ctx.Error(http.StatusInternalServerError, "ToOrganization", err)
	} else {
		ctx.JSON(http.StatusCreated, apiOrg)
	}
}

func Edit(ctx *context.APIContext) {
	form := web.GetForm(ctx).(*hat_api.EditOrgOption)
	org := ctx.Org.Organization
	nameChanged := org.Name != form.Name

	if org.LowerName != strings.ToLower(form.Name) {
		if len(form.Name) > 40 {
			ctx.Error(http.StatusBadRequest, "EditOrganization", "name长度不能超过40个字符")
			return
		}

		isExist, err := user_model.IsUserExist(ctx, org.ID, form.Name)
		if err != nil {
			ctx.Error(http.StatusConflict, "IsUserExist", ctx.Tr("form.username_been_taken"))
			return
		} else if isExist {
			ctx.Error(http.StatusConflict, "IsUserExist", ctx.Tr("form.username_been_taken"))
			return
		} else if err = user_model.ChangeUserName(org.AsUser(), form.Name); err != nil {
			switch {
			case db.IsErrNameReserved(err):
				ctx.Error(http.StatusBadRequest, "EditOrganization", ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name))
				return
			case db.IsErrNamePatternNotAllowed(err):
				ctx.Error(http.StatusBadRequest, "EditOrganization", ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern))
				return
			default:
				ctx.Error(http.StatusInternalServerError, "ChangeUserName", err)
				return
			}
		}

		if err := container_service.UpdateRepositoryNames(ctx, org.AsUser(), form.Name); err != nil {
			ctx.ServerError("UpdateRepositoryNames", err)
			return
		}

		nameChanged = false
	}

	org.Name = form.Name
	org.LowerName = strings.ToLower(form.Name)
	org.FullName = form.FullName
	org.Description = form.Description
	org.Website = form.Website
	org.Location = form.Location
	visibilityChanged := api.VisibilityModes[form.Visibility] != org.Visibility

	if form.Visibility != "" {
		org.Visibility = api.VisibilityModes[form.Visibility]
	}
	if form.RepoAdminChangeTeamAccess != nil {
		org.RepoAdminChangeTeamAccess = *form.RepoAdminChangeTeamAccess
	}

	if err := user_model.UpdateUser(ctx, org.AsUser(), false); err != nil {
		ctx.ServerError("UpdateUser", err)
		return
	}
	if visibilityChanged {
		repos, _, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
			Actor: org.AsUser(), Private: true, ListOptions: db.ListOptions{Page: 1, PageSize: org.NumRepos},
		})
		if err != nil {
			ctx.ServerError("GetRepositories", err)
			return
		}
		for _, repo := range repos {
			repo.OwnerName = org.Name
			if err := repo_service.UpdateRepository(repo, true); err != nil {
				ctx.ServerError("UpdateRepository", err)
				return
			}
		}
	} else if nameChanged {
		if err := repo_model.UpdateRepositoryOwnerNames(org.ID, org.Name); err != nil {
			ctx.ServerError("UpdateRepository", err)
			return
		}
	}

	ctx.JSON(http.StatusOK, convert.ToOrganization(org))
}
