// Copyright 2016 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 models

import (
	"time"
	"fmt"

	"github.com/go-xorm/xorm"

	git "github.com/gogits/git-module"
)

type Commit struct {
	ID              int64
	RepoID          int64  `xorm:"UNIQUE(commit_repo_sha)"`
	Sha             string `xorm:"VARCHAR(40) UNIQUE(commit_repo_sha)"`
	Message         string `xorm:"TEXT FULLTEXT(commit_message)"`
	AuthorEmail     string
	AuthorName      string
	AuthorTime      time.Time
	CommitterEmail  string
	CommitterName   string
	CommitterTime   time.Time
	Repo            *Repository `xorm:"-"`
}

func GitCommitToSearchCommit(repoID int64, commit *git.Commit) *Commit {
	return &Commit{
		RepoID:         repoID,
		Sha:            commit.ID.String(),
		Message:        commit.Message(),
		AuthorEmail:    commit.Author.Email,
		AuthorName:     commit.Author.Name,
		AuthorTime:     commit.Author.When,
		CommitterEmail: commit.Committer.Email,
		CommitterName:  commit.Committer.Name,
		CommitterTime:  commit.Committer.When,
	}
}

func FulltextSearchCommits(userid int64, q string, limit int, offset int) ([]*Commit, int64, error) {
	sess := x.NewSession()
	sess.Join("INNER", "repository", "repository.id = commit.repo_id")
	if userid > 0 {
		sess.Join("LEFT", "access", "access.repo_id = commit.repo_id AND access.user_id=?", userid)
		sess.Where("(NOT repository.is_private OR access.user_id IS NOT NULL OR repository.owner_id=?)", userid)
	} else {
		sess.Where("NOT repository.is_private")
	}
	if q != "" {
		cond, params := x.FulltextMatch("message", q)
		sess.Where(cond, params...)
	}
	var countSess xorm.Session
	countSess = *sess
	count, err := countSess.Count(new(Commit))
	if err != nil {
		return nil, 0, fmt.Errorf("Count: %v", err)
	}
	sess.OrderBy("commit.committer_time DESC")
	sess.Limit(limit, offset)
	commits := make([]*Commit, 0)
	if err := sess.Find(&commits); err != nil {
		return nil, 0, fmt.Errorf("Find: %v", err)
	}
	if len(commits) == 0 {
		return commits, count, nil
	}
	// Load repositories
	set := make(map[int64]*Repository)
	for i := range commits {
		set[commits[i].RepoID] = nil
	}
	ids := make([]int64, 0, len(set))
	for i := range set {
		ids = append(ids, i)
	}
	repos := make([]*Repository, 0, len(ids))
	if err := sess.In("id", ids).Find(&repos); err != nil {
		return nil, 0, fmt.Errorf("Find repos: %v", err)
	}
	if err = RepositoryList(repos).LoadAttributes(); err != nil {
		return nil, 0, fmt.Errorf("Load repo attrs: %v", err)
	}
	for i := range repos {
		set[repos[i].ID] = repos[i]
	}
	for i := range commits {
		commits[i].Repo = set[commits[i].RepoID]
	}
	return commits, count, nil
}
