package pulls

import (
	"context"
	"net/http"
	"strconv"

	"tangled.org/core/api/tangled"
	"tangled.org/core/appview/db"
	"tangled.org/core/appview/models"
	"tangled.org/core/appview/pages"
	"tangled.org/core/xrpc/xrpcclient"

	"github.com/bluesky-social/indigo/atproto/syntax"
	indigoxrpc "github.com/bluesky-social/indigo/xrpc"
	"github.com/go-chi/chi/v5"
)

// htmx fragment
func (s *Pulls) PullActions(w http.ResponseWriter, r *http.Request) {
	l := s.logger.With("handler", "PullActions")

	switch r.Method {
	case http.MethodGet:
		user := s.oauth.GetMultiAccountUser(r)
		if user != nil {
			l = l.With("user", user.Did)
		}

		f, err := s.repoResolver.Resolve(r)
		if err != nil {
			l.Error("failed to get repo and knot", "err", err)
			return
		}

		pull, ok := r.Context().Value("pull").(*models.Pull)
		if !ok {
			l.Error("failed to get pull")
			s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
			return
		}
		l = l.With("pull_id", pull.PullId, "pull_owner", pull.OwnerDid)

		versionNumber, err := strconv.Atoi(chi.URLParam(r, "version"))
		if err != nil {
			versionNumber = pull.LatestVersionNumber()
		}
		if versionNumber >= len(pull.Versions) {
			http.Error(w, "bad round id", http.StatusBadRequest)
			l.Error("failed to parse round id", "err", err, "round_number", versionNumber)
			return
		}

		// only the last round's buttons and banners use merge/resubmit checks
		var mergeCheckParams pages.MergeCheckParams
		var resubmitResult = pages.Unknown
		if versionNumber == pull.LatestVersionNumber() {
			mergeCheckParams = s.composeMergeCheck(r.Context(), f, pull.TargetBranch, pull.SourceRepo, pull.LatestVersion().Head)
			if user != nil && syntax.DID(user.Did) == pull.OwnerDid {
				resubmitResult = s.resubmitCheck(r.Context(), pull)
			}
		}

		s.pages.PullActionsFragment(w, pages.PullActionsParams{
			BaseParams:         pages.BaseParamsFromContext(r.Context()),
			RepoInfo:           s.repoResolver.GetRepoInfo(r, user),
			Pull:               pull,
			RoundNumber:        versionNumber,
			MergeCheck:         mergeCheckParams,
			ResubmitCheck:      resubmitResult,
			BranchDeleteStatus: s.branchDeleteStatus(r, f, pull),
		})
		return
	}
}

func (s *Pulls) branchDeleteStatus(r *http.Request, repo *models.Repo, pull *models.Pull) *pages.BranchDeleteStatus {
	if pull.State != models.PullMerged || pull.SourceBranch == nil {
		return nil
	}

	user := s.oauth.GetMultiAccountUser(r)
	if user == nil {
		return nil
	}

	branch := *pull.SourceBranch

	if syntax.DID(repo.RepoDid) != pull.SourceRepo {
		var err error
		repo, err = db.GetRepoByDid(s.db, pull.SourceRepo.String())
		if err != nil {
			return nil
		}
	}

	// user can only delete branch if they are a collaborator in the repo that the branch belongs to
	if !s.acl.HasRepoPermission(r.Context(), repo, user.Did, "repo:push") {
		return nil
	}

	xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url}
	resp, err := tangled.GitTempGetBranch(r.Context(), xrpcc, branch, repo.RepoDid)
	if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
		s.logger.Error("failed to get branch", "xrpcerr", xrpcerr, "err", err)
		return nil
	}

	return &pages.BranchDeleteStatus{
		Repo:   repo,
		Branch: resp.Name,
	}
}

func (s *Pulls) resubmitCheck(ctx context.Context, pull *models.Pull) pages.ResubmitResult {
	if pull.State == models.PullMerged || pull.State == models.PullAbandoned || pull.SourceBranch == nil {
		return pages.Unknown
	}

	xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url}
	branchResp, err := tangled.GitTempGetBranch(ctx, xrpcc, *pull.SourceBranch, pull.SourceRepo.String())
	if err != nil {
		if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
			s.logger.Error("failed to call XRPC repo.branches", "xrpcerr", xrpcerr, "err", err, "pull_id", pull.PullId, "branch", *pull.SourceBranch)
			return pages.Unknown
		}
		s.logger.Error("failed to reach knotserver", "err", err, "pull_id", pull.PullId)
		return pages.Unknown
	}

	targetBranch := branchResp

	if pull.LatestVersion().Head != targetBranch.Hash {
		return pages.ShouldResubmit
	}

	return pages.ShouldNotResubmit
}
