package github

import (
	"fmt"
	"strconv"

	"github.com/tidbcloud/devops/api"
	githubapi "github.com/tidbcloud/devops/api/github"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/clients/githubcli"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	ghsvc "github.com/tidbcloud/devops/pkg/service/github"

	"github.com/gin-gonic/gin"
)

// RawListPullRequests
//
//	@Id				GithubControllerRawListPullRequests
//	@Description	list pull requests from github directly
//	@Tags			GithubController
//	@Accept			json
//	@Produce		json
//	@Param			owner	path		string								true	"owner name"
//	@Param			repo	path		string								true	"repo name"
//	@Param			req		query		githubapi.RawListPullRequestsReq	false	"request param"
//	@Success		200		{object}	githubapi.RawListPullRequestsResp
//	@Router			/api/v1/devops/github/raw/repos/{owner}/{repo}/pulls [get]
func (c *GithubController) RawListPullRequests(ctx *gin.Context) {
	owner := ctx.Param("owner")
	repo := ctx.Param("repo")
	if owner == "" || repo == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("owner and repo are required"), api.ErrCodeInvalidArgument)
		return
	}

	req := &githubapi.RawListPullRequestsReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	opt := githubcli.ListPullRequestOption{
		Pager:  cvtPager(req.CursorPagerReq),
		Labels: req.Labels,
	}
	result, err := c.svc.RawAPI().PullRequest.List(ctx, owner, repo, opt)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := githubapi.RawListPullRequestsResp{
		Page: cvtPagerResultToAPI(result.Page),
		PRs:  container.TransformSlice(result.PullRequests, ghsvc.ConvertPullRequest),
	}

	ctrl.Success(ctx, resp)
}

// RawGetPullRequest
//
//	@Id				GithubControllerRawGetPullRequest
//	@Description	get a pull request from github directly
//	@Tags			GithubController
//	@Accept			json
//	@Produce		json
//	@Param			owner		path		string	true	"owner name"
//	@Param			repo		path		string	true	"repo name"
//	@Param			pr_number	path		int		true	"pr number"
//	@Success		200			{object}	githubapi.RawGetPullRequestResp
//	@Router			/api/v1/devops/github/raw/repos/{owner}/{repo}/pulls/{pr_number} [get]
func (c *GithubController) RawGetPullRequest(ctx *gin.Context) {
	owner := ctx.Param("owner")
	repo := ctx.Param("repo")
	if owner == "" || repo == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("owner and repo are required"), api.ErrCodeInvalidArgument)
		return
	}

	prNumber, err := strconv.Atoi(ctx.Param("pr_number"))
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("pr_number is invalid"), api.ErrCodeInvalidArgument)
	}

	pr, err := c.svc.RawAPI().PullRequest.Get(ctx, owner, repo, prNumber)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := githubapi.RawGetPullRequestResp{
		PR: ghsvc.ConvertPullRequest(*pr),
	}

	ctrl.Success(ctx, resp)
}

// RawListReleases
//
//	@Id				GithubControllerRawListReleases
//	@Description	list releases from github directly
//	@Tags			GithubController
//	@Accept			json
//	@Produce		json
//	@Param			owner	path		string							true	"owner name"
//	@Param			repo	path		string							true	"repo name"
//	@Param			req		query		githubapi.RawListReleasesReq	false	"request param"
//	@Success		200		{object}	githubapi.RawListReleasesResp
//	@Router			/api/v1/devops/github/raw/repos/{owner}/{repo}/releases [get]
func (c *GithubController) RawListReleases(ctx *gin.Context) {
	owner := ctx.Param("owner")
	repo := ctx.Param("repo")
	if owner == "" || repo == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("owner and repo are required"), api.ErrCodeInvalidArgument)
		return
	}

	req := &githubapi.RawListReleasesReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	opt := githubcli.ListReleaseOption{
		Pager: cvtPager(req.CursorPagerReq),
	}
	result, err := c.svc.RawAPI().Release.List(ctx, owner, repo, opt)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := githubapi.RawListReleasesResp{
		Page:     cvtPagerResultToAPI(result.Page),
		Releases: container.TransformSlice(result.Releases, ghsvc.ConvertRelease),
	}

	ctrl.Success(ctx, resp)
}

// RawGetRelease
//
//	@Id				GithubControllerRawGetRelease
//	@Description	get a release from github directly
//	@Tags			GithubController
//	@Accept			json
//	@Produce		json
//	@Param			owner		path		string	true	"owner name"
//	@Param			repo		path		string	true	"repo name"
//	@Param			release_tag	path		string	true	"the tag name"
//	@Success		200			{object}	githubapi.RawGetReleaseResp
//	@Router			/api/v1/devops/github/raw/repos/{owner}/{repo}/releases/{release_tag} [get]
func (c *GithubController) RawGetRelease(ctx *gin.Context) {
	owner := ctx.Param("owner")
	repo := ctx.Param("repo")
	tag := ctx.Param("release_tag")
	if owner == "" || repo == "" || tag == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("owner and repo are required"), api.ErrCodeInvalidArgument)
		return
	}

	release, err := c.svc.RawAPI().Release.Get(ctx, owner, repo, tag)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := githubapi.RawGetReleaseResp{
		Release: ghsvc.ConvertRelease(*release),
	}

	ctrl.Success(ctx, resp)
}

// RawDiffRelease
//
//	@Id				GithubControllerRawDiffRelease
//	@Description	get pull requests between two releases from github directly
//	@Tags			GithubController
//	@Accept			json
//	@Produce		json
//	@Param			owner	path		string						true	"owner name"
//	@Param			repo	path		string						true	"repo name"
//	@Param			req		query		githubapi.RawDiffReleaseReq	false	"request param"
//	@Success		200		{object}	githubapi.RawDiffReleaseResp
//	@Router			/api/v1/devops/github/raw/repos/{owner}/{repo}/releases/diff [get]
func (c *GithubController) RawDiffRelease(ctx *gin.Context) {
	owner := ctx.Param("owner")
	repo := ctx.Param("repo")
	if owner == "" || repo == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("owner and repo are required"), api.ErrCodeInvalidArgument)
		return
	}

	req := &githubapi.RawDiffReleaseReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	prs, err := c.svc.RawAPI().Release.Diff(ctx, owner, repo, req.Target, req.Prev)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := githubapi.RawDiffReleaseResp{
		PRs: container.TransformSlice(prs, ghsvc.ConvertPullRequest),
	}

	ctrl.Success(ctx, resp)
}

// RawListLabels
//
//	@Id				GithubControllerRawListLabels
//	@Description	list labels of a repo from github directly
//	@Tags			GithubController
//	@Accept			json
//	@Produce		json
//	@Param			owner	path		string						true	"owner name"
//	@Param			repo	path		string						true	"repo name"
//	@Param			req		query		githubapi.RawListLabelsReq	false	"request param"
//	@Success		200		{object}	githubapi.RawListLabelsResp
//	@Router			/api/v1/devops/github/raw/repos/{owner}/{repo}/labels [get]
func (c *GithubController) RawListLabels(ctx *gin.Context) {
	owner := ctx.Param("owner")
	repo := ctx.Param("repo")
	if owner == "" || repo == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("owner and repo are required"), api.ErrCodeInvalidArgument)
		return
	}

	req := &githubapi.RawListLabelsReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	opt := githubcli.ListLabelOption{
		Pager: cvtPager(req.CursorPagerReq),
	}
	result, err := c.svc.RawAPI().Repository.ListLabels(ctx, owner, repo, opt)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := githubapi.RawListLabelsResp{
		Page:   cvtPagerResultToAPI(result.Page),
		Labels: container.TransformSlice(result.Labels, ghsvc.ConvertLabel),
	}

	ctrl.Success(ctx, resp)
}

// RawGetLabel
//
//	@Id				GithubControllerRawGetLabel
//	@Description	get a label from github directly
//	@Tags			GithubController
//	@Accept			json
//	@Produce		json
//	@Param			owner		path		string	true	"owner name"
//	@Param			repo		path		string	true	"repo name"
//	@Param			label_name	path		string	true	"label name"
//	@Success		200			{object}	githubapi.RawGetLabelResp
//	@Router			/api/v1/devops/github/raw/repos/{owner}/{repo}/labels/{label_name} [get]
func (c *GithubController) RawGetLabel(ctx *gin.Context) {
	owner := ctx.Param("owner")
	repo := ctx.Param("repo")
	name := ctx.Param("label_name")
	if owner == "" || repo == "" || name == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("owner and repo are required"), api.ErrCodeInvalidArgument)
		return
	}

	label, err := c.svc.RawAPI().Repository.GetLabel(ctx, owner, repo, name)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := githubapi.RawGetLabelResp{
		Label: ghsvc.ConvertLabel(*label),
	}

	ctrl.Success(ctx, resp)
}
