package web

import (
	"net/http"

	"github.com/charmbracelet/log/v2"
	"github.com/charmbracelet/soft-serve/git"
	"github.com/charmbracelet/soft-serve/pkg/config"
	"github.com/charmbracelet/soft-serve/pkg/proto"
	"github.com/gorilla/mux"
)

// CommitData contains data for rendering individual commit view.
type CommitData struct {
	RepoBaseData
	Commit    *git.Commit
	Diff      *git.Diff
	ParentIDs []string
}

func repoCommit(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	logger := log.FromContext(ctx)
	cfg := config.FromContext(ctx)
	repo := proto.RepositoryFromContext(ctx)
	vars := mux.Vars(r)
	hash := vars["hash"]

	gr, err := openRepository(repo)
	if err != nil {
		logger.Debug("failed to open repository", "repo", repo.Name(), "err", err)
		renderInternalServerError(w, r)
		return
	}

	commit, err := gr.CatFileCommit(hash)
	if err != nil {
		logger.Debug("failed to get commit", "repo", repo.Name(), "hash", hash, "err", err)
		renderNotFound(w, r)
		return
	}

	diff, err := gr.Diff(commit)
	if err != nil {
		logger.Debug("failed to get diff", "repo", repo.Name(), "hash", hash, "err", err)
		renderInternalServerError(w, r)
		return
	}

	defaultBranch := getDefaultBranch(gr)

	parentIDs := make([]string, commit.ParentsCount())
	for i := 0; i < commit.ParentsCount(); i++ {
		parentID, err := commit.ParentID(i)
		if err == nil && parentID != nil {
			parentIDs[i] = parentID.String()
		}
	}

	data := CommitData{
		RepoBaseData: RepoBaseData{
			BaseData: BaseData{
				ServerName: cfg.Name,
				ActiveTab:  "commits",
			},
			Repo:          repo,
			DefaultBranch: defaultBranch,
		},
		Commit:    commit,
		Diff:      diff,
		ParentIDs: parentIDs,
	}

	renderHTML(w, "commit.html", data)
}

func repoCommitPatch(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	logger := log.FromContext(ctx)
	repo := proto.RepositoryFromContext(ctx)
	vars := mux.Vars(r)
	hash := vars["hash"]

	gr, err := openRepository(repo)
	if err != nil {
		logger.Debug("failed to open repository", "repo", repo.Name(), "err", err)
		renderInternalServerError(w, r)
		return
	}

	_, err = gr.CatFileCommit(hash)
	if err != nil {
		logger.Debug("failed to get commit", "repo", repo.Name(), "hash", hash, "err", err)
		renderNotFound(w, r)
		return
	}

	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	if err := gr.RawDiff(hash, git.RawDiffPatch, w); err != nil {
		logger.Debug("failed to generate patch", "repo", repo.Name(), "hash", hash, "err", err)
	}
}

func repoCommitDiff(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	logger := log.FromContext(ctx)
	repo := proto.RepositoryFromContext(ctx)
	vars := mux.Vars(r)
	hash := vars["hash"]

	gr, err := openRepository(repo)
	if err != nil {
		logger.Debug("failed to open repository", "repo", repo.Name(), "err", err)
		renderInternalServerError(w, r)
		return
	}

	_, err = gr.CatFileCommit(hash)
	if err != nil {
		logger.Debug("failed to get commit", "repo", repo.Name(), "hash", hash, "err", err)
		renderNotFound(w, r)
		return
	}

	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	if err := gr.RawDiff(hash, git.RawDiffNormal, w); err != nil {
		logger.Debug("failed to generate diff", "repo", repo.Name(), "hash", hash, "err", err)
	}
}
