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

package badges

import (
	"fmt"
	"net/url"
	"strings"

	actions_model "forgejo.org/models/actions"
	repo_model "forgejo.org/models/repo"
	"forgejo.org/models/unit"
	"forgejo.org/modules/setting"
	app_context "forgejo.org/services/context"
)

func getBadgeURL(ctx *app_context.Context, label, text, color string) string {
	sb := &strings.Builder{}
	_ = setting.Badges.GeneratorURLTemplateTemplate.Execute(sb, map[string]string{
		"label": url.PathEscape(strings.ReplaceAll(label, "-", "--")),
		"text":  url.PathEscape(strings.ReplaceAll(text, "-", "--")),
		"color": url.PathEscape(color),
	})

	badgeURL := sb.String()
	q := ctx.Req.URL.Query()
	// Remove any `branch` or `event` query parameters. They're used by the
	// workflow badge route, and do not need forwarding to the badge generator.
	delete(q, "branch")
	delete(q, "event")
	if len(q) > 0 {
		return fmt.Sprintf("%s?%s", badgeURL, q.Encode())
	}
	return badgeURL
}

func redirectToBadge(ctx *app_context.Context, label, text, color string) {
	ctx.Redirect(getBadgeURL(ctx, label, text, color))
}

func errorBadge(ctx *app_context.Context, label, text string) { //nolint:unparam
	ctx.Redirect(getBadgeURL(ctx, label, text, "crimson"))
}

func GetWorkflowBadge(ctx *app_context.Context) {
	branch := ctx.Req.URL.Query().Get("branch")
	if branch != "" {
		branch = fmt.Sprintf("refs/heads/%s", branch)
	}
	event := ctx.Req.URL.Query().Get("event")

	workflowFile := ctx.Params("workflow_name")
	run, err := actions_model.GetLatestRunForBranchAndWorkflow(ctx, ctx.Repo.Repository.ID, branch, workflowFile, event)
	if err != nil {
		errorBadge(ctx, workflowFile, "Not found")
		return
	}

	var color string
	switch run.Status {
	case actions_model.StatusUnknown:
		color = "lightgrey"
	case actions_model.StatusWaiting:
		color = "lightgrey"
	case actions_model.StatusRunning:
		color = "gold"
	case actions_model.StatusSuccess:
		color = "brightgreen"
	case actions_model.StatusFailure:
		color = "crimson"
	case actions_model.StatusCancelled:
		color = "orange"
	case actions_model.StatusSkipped:
		color = "blue"
	case actions_model.StatusBlocked:
		color = "yellow"
	default:
		color = "lightgrey"
	}

	redirectToBadge(ctx, workflowFile, run.Status.String(), color)
}

func getIssueOrPullBadge(ctx *app_context.Context, label, variant string, num int) {
	var text string
	if len(variant) > 0 {
		text = fmt.Sprintf("%d %s", num, variant)
	} else {
		text = fmt.Sprintf("%d", num)
	}
	redirectToBadge(ctx, label, text, "blue")
}

func getIssueBadge(ctx *app_context.Context, variant string, num int) {
	if !ctx.Repo.CanRead(unit.TypeIssues) &&
		!ctx.Repo.CanRead(unit.TypeExternalTracker) {
		errorBadge(ctx, "issues", "Not found")
		return
	}

	_, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker)
	if err == nil {
		errorBadge(ctx, "issues", "Not found")
		return
	}

	getIssueOrPullBadge(ctx, "issues", variant, num)
}

func getPullBadge(ctx *app_context.Context, variant string, num int) {
	if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
		errorBadge(ctx, "pulls", "Not found")
		return
	}

	getIssueOrPullBadge(ctx, "pulls", variant, num)
}

func GetOpenIssuesBadge(ctx *app_context.Context) {
	getIssueBadge(ctx, "open", ctx.Repo.Repository.NumOpenIssues(ctx))
}

func GetClosedIssuesBadge(ctx *app_context.Context) {
	getIssueBadge(ctx, "closed", ctx.Repo.Repository.NumClosedIssues(ctx))
}

func GetTotalIssuesBadge(ctx *app_context.Context) {
	getIssueBadge(ctx, "", ctx.Repo.Repository.NumIssues(ctx))
}

func GetOpenPullsBadge(ctx *app_context.Context) {
	getPullBadge(ctx, "open", ctx.Repo.Repository.NumOpenPulls(ctx))
}

func GetClosedPullsBadge(ctx *app_context.Context) {
	getPullBadge(ctx, "closed", ctx.Repo.Repository.NumClosedPulls(ctx))
}

func GetTotalPullsBadge(ctx *app_context.Context) {
	getPullBadge(ctx, "", ctx.Repo.Repository.NumPulls(ctx))
}

func GetStarsBadge(ctx *app_context.Context) {
	redirectToBadge(ctx, "stars", fmt.Sprintf("%d", ctx.Repo.Repository.NumStars), "blue")
}

func GetLatestReleaseBadge(ctx *app_context.Context) {
	release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID)
	if err != nil {
		if repo_model.IsErrReleaseNotExist(err) {
			errorBadge(ctx, "release", "Not found")
			return
		}
		ctx.ServerError("GetLatestReleaseByRepoID", err)
	}

	if err := release.LoadAttributes(ctx); err != nil {
		ctx.ServerError("LoadAttributes", err)
		return
	}

	redirectToBadge(ctx, "release", release.TagName, "blue")
}
