// Copyright 2018 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"net/http"
	"strings"
	"time"

	"github.com/gogs/git-module"
	api "github.com/gogs/go-gogs-client"

	"gogs.io/gogs/internal/conf"
	"gogs.io/gogs/internal/context"
	"gogs.io/gogs/internal/db"
	"gogs.io/gogs/internal/gitutil"
)

func GetSingleCommit(c *context.APIContext) {
	if strings.Contains(c.Req.Header.Get("Accept"), api.MediaApplicationSHA) {
		c.SetParams("*", c.Params(":sha"))
		GetReferenceSHA(c)
		return
	}

	gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
	if err != nil {
		c.Error(err, "open repository")
		return
	}
	commit, err := gitRepo.CatFileCommit(c.Params(":sha"))
	if err != nil {
		c.NotFoundOrError(gitutil.NewError(err), "get commit")
		return
	}

	// Retrieve author and committer information
	var apiAuthor, apiCommitter *api.User
	author, err := db.GetUserByEmail(commit.Author.Email)
	if err != nil && !db.IsErrUserNotExist(err) {
		c.Error(err, "get user by author email")
		return
	} else if err == nil {
		apiAuthor = author.APIFormat()
	}
	// Save one query if the author is also the committer
	if commit.Committer.Email == commit.Author.Email {
		apiCommitter = apiAuthor
	} else {
		committer, err := db.GetUserByEmail(commit.Committer.Email)
		if err != nil && !db.IsErrUserNotExist(err) {
			c.Error(err, "get user by committer email")
			return
		} else if err == nil {
			apiCommitter = committer.APIFormat()
		}
	}

	// Retrieve parent(s) of the commit
	apiParents := make([]*api.CommitMeta, commit.ParentsCount())
	for i := 0; i < commit.ParentsCount(); i++ {
		sha, _ := commit.ParentID(i)
		apiParents[i] = &api.CommitMeta{
			URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(),
			SHA: sha.String(),
		}
	}

	c.JSONSuccess(&api.Commit{
		CommitMeta: &api.CommitMeta{
			URL: conf.Server.ExternalURL + c.Link[1:],
			SHA: commit.ID.String(),
		},
		HTMLURL: c.Repo.Repository.HTMLURL() + "/commits/" + commit.ID.String(),
		RepoCommit: &api.RepoCommit{
			URL: conf.Server.ExternalURL + c.Link[1:],
			Author: &api.CommitUser{
				Name:  commit.Author.Name,
				Email: commit.Author.Email,
				Date:  commit.Author.When.Format(time.RFC3339),
			},
			Committer: &api.CommitUser{
				Name:  commit.Committer.Name,
				Email: commit.Committer.Email,
				Date:  commit.Committer.When.Format(time.RFC3339),
			},
			Message: commit.Summary(),
			Tree: &api.CommitMeta{
				URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/tree/" + commit.ID.String(),
				SHA: commit.ID.String(),
			},
		},
		Author:    apiAuthor,
		Committer: apiCommitter,
		Parents:   apiParents,
	})
}

func GetReferenceSHA(c *context.APIContext) {
	gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
	if err != nil {
		c.Error(err, "open repository")
		return
	}

	ref := c.Params("*")
	refType := 0 // 0-unknown, 1-branch, 2-tag
	if strings.HasPrefix(ref, git.RefsHeads) {
		ref = strings.TrimPrefix(ref, git.RefsHeads)
		refType = 1
	} else if strings.HasPrefix(ref, git.RefsTags) {
		ref = strings.TrimPrefix(ref, git.RefsTags)
		refType = 2
	} else {
		if gitRepo.HasBranch(ref) {
			refType = 1
		} else if gitRepo.HasTag(ref) {
			refType = 2
		} else {
			c.NotFound()
			return
		}
	}

	var sha string
	if refType == 1 {
		sha, err = gitRepo.BranchCommitID(ref)
	} else if refType == 2 {
		sha, err = gitRepo.TagCommitID(ref)
	}
	if err != nil {
		c.NotFoundOrError(gitutil.NewError(err), "get reference commit ID")
		return
	}
	c.PlainText(http.StatusOK, sha)
}
<html>
<head><title>PANIC: session(release): write data/sessions/1/c/1c30a16b2562c8b4: no space left on device</title>
<meta charset="utf-8" />
<style type="text/css">
html, body {
	font-family: "Roboto", sans-serif;
	color: #333333;
	background-color: #ea5343;
	margin: 0px;
}
h1 {
	color: #d04526;
	background-color: #ffffff;
	padding: 20px;
	border-bottom: 1px dashed #2b3848;
}
pre {
	margin: 20px;
	padding: 20px;
	border: 2px solid #2b3848;
	background-color: #ffffff;
	white-space: pre-wrap;       /* css-3 */
	white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
	white-space: -pre-wrap;      /* Opera 4-6 */
	white-space: -o-pre-wrap;    /* Opera 7 */
	word-wrap: break-word;       /* Internet Explorer 5.5+ */
}
</style>
</head><body>
<h1>PANIC</h1>
<pre style="font-weight: bold;">session(release): write data/sessions/1/c/1c30a16b2562c8b4: no space left on device</pre>
<pre>github.com/go-macaron/session@v0.0.0-20190805070824-1a3cdc6f5659/session.go:199 (0x8b2934)
gopkg.in/macaron.v1@v1.3.9/context.go:79 (0x83d0a0)
github.com/go-macaron/inject@v0.0.0-20160627170012-d8a0b8677191/inject.go:157 (0x80ab07)
github.com/go-macaron/inject@v0.0.0-20160627170012-d8a0b8677191/inject.go:135 (0x80a8a8)
gopkg.in/macaron.v1@v1.3.9/context.go:121 (0x83d1f8)
gopkg.in/macaron.v1@v1.3.9/context.go:112 (0x84fdb5)
gopkg.in/macaron.v1@v1.3.9/recovery.go:161 (0x84fda8)
gopkg.in/macaron.v1@v1.3.9/logger.go:40 (0x840c73)
github.com/go-macaron/inject@v0.0.0-20160627170012-d8a0b8677191/inject.go:157 (0x80ab07)
github.com/go-macaron/inject@v0.0.0-20160627170012-d8a0b8677191/inject.go:135 (0x80a8a8)
gopkg.in/macaron.v1@v1.3.9/context.go:121 (0x83d1f8)
gopkg.in/macaron.v1@v1.3.9/router.go:187 (0x850fc6)
gopkg.in/macaron.v1@v1.3.9/router.go:303 (0x8493e5)
gopkg.in/macaron.v1@v1.3.9/macaron.go:220 (0x841fca)
net/http/server.go:2836 (0x7a79b2)
net/http/server.go:1924 (0x7a341b)
runtime/asm_amd64.s:1373 (0x46f9f0)
</pre>
</body>
</html>