package activities

import (
	"fmt"
	"time"

	gitea_activities_models "code.gitea.io/gitea/models/activities"
	"code.gitea.io/gitea/models/db"
	"code.gitea.io/gitea/models/organization"
	gitea_repo_model "code.gitea.io/gitea/models/repo"
	gitea_user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/modules/structs"

	"xorm.io/builder"
)

type Contributor struct {
	ID            int64  `json:"id"`
	Name          string `json:"name"`
	Email         string `json:"email"`
	Contributions int64  `json:"contributions"`
}

func GetContributors(repoID, userID int64) (result []Contributor, err error) {
	sql :=
		`select a.act_user_id as id ,
			u.name as login,
			u.email as email,
			count(act_user_id) as contributions
		from action a
		left join user  u  on a.act_user_id=u.id
		where repo_id=? and user_id=?
		group by repo_id,act_user_id `

	err = db.GetEngine(db.DefaultContext).SQL(sql, repoID, userID).Find(&result)
	return
}

func activityQueryCondition(opts gitea_activities_models.GetFeedsOptions) (builder.Cond, error) {
	cond := builder.NewCond()

	if opts.RequestedTeam != nil && opts.RequestedUser == nil {
		org, err := gitea_user_model.GetUserByID(opts.RequestedTeam.OrgID)
		if err != nil {
			return nil, err
		}
		opts.RequestedUser = org
	}

	// check activity visibility for actor ( similar to activityReadable() )
	if opts.Actor == nil {
		cond = cond.And(builder.In("act_user_id",
			builder.Select("`user`.id").Where(
				builder.Eq{"keep_activity_private": false, "visibility": structs.VisibleTypePublic},
			).From("`user`"),
		))
	} else if !opts.Actor.IsAdmin {
		cond = cond.And(builder.In("act_user_id",
			builder.Select("`user`.id").Where(
				builder.Eq{"keep_activity_private": false}.
					And(builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))).
				Or(builder.Eq{"id": opts.Actor.ID}).From("`user`"),
		))
	}

	// check readable repositories by doer/actor
	if opts.Actor == nil || !opts.Actor.IsAdmin {
		cond = cond.And(builder.In("repo_id", gitea_repo_model.AccessibleRepoIDsQuery(opts.Actor)))
	}

	if opts.RequestedRepo != nil {
		cond = cond.And(builder.Eq{"repo_id": opts.RequestedRepo.ID})
	}

	if opts.RequestedTeam != nil {
		env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(opts.RequestedTeam)
		teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
		if err != nil {
			return nil, fmt.Errorf("GetTeamRepositories: %w", err)
		}
		cond = cond.And(builder.In("repo_id", teamRepoIDs))
	}

	if opts.RequestedUser != nil {
		cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})

		if opts.OnlyPerformedBy {
			cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
		}
	}

	if !opts.IncludePrivate {
		cond = cond.And(builder.Eq{"`action`.is_private": false})
	}
	if !opts.IncludeDeleted {
		cond = cond.And(builder.Eq{"is_deleted": false})
	}

	if opts.Date != "" {
		dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
		if err != nil {
			log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
		} else {
			dateHigh := dateLow.Add(86399000000000) // 23h59m59s

			cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
			cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
		}
	}

	return cond, nil
}
