// Copyright 2021 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gitlab

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"net/http"
	"strings"

	"github.com/woodpecker-ci/woodpecker/server/model"

	"github.com/xanzy/go-gitlab"
)

func (g *Gitlab) convertGitlabRepo(repo_ *gitlab.Project) (*model.Repo, error) {
	parts := strings.Split(repo_.PathWithNamespace, "/")
	// TODO: save repo id (support nested repos)
	var owner = parts[0]
	var name = parts[1]
	repo := &model.Repo{
		Owner:      owner,
		Name:       name,
		FullName:   repo_.PathWithNamespace,
		Avatar:     repo_.AvatarURL,
		Link:       repo_.WebURL,
		Clone:      repo_.HTTPURLToRepo,
		Branch:     repo_.DefaultBranch,
		Visibility: string(repo_.Visibility),
	}

	if len(repo.Branch) == 0 { // TODO: do we need that?
		repo.Branch = "master"
	}

	if len(repo.Avatar) != 0 && !strings.HasPrefix(repo.Avatar, "http") {
		repo.Avatar = fmt.Sprintf("%s/%s", g.URL, repo.Avatar)
	}

	if g.PrivateMode {
		repo.IsPrivate = true
	} else {
		repo.IsPrivate = !repo_.Public
	}

	return repo, nil
}

func convertMergeRequestHock(hook *gitlab.MergeEvent, req *http.Request) (*model.Repo, *model.Build, error) {
	repo := &model.Repo{}
	build := &model.Build{}

	target := hook.ObjectAttributes.Target
	source := hook.ObjectAttributes.Source
	obj := hook.ObjectAttributes

	if target == nil && source == nil {
		return nil, nil, fmt.Errorf("target and source keys expected in merge request hook")
	} else if target == nil {
		return nil, nil, fmt.Errorf("target key expected in merge request hook")
	} else if source == nil {
		return nil, nil, fmt.Errorf("source key exptected in merge request hook")
	}

	if target.PathWithNamespace != "" {
		var err error
		if repo.Owner, repo.Name, err = extractFromPath(target.PathWithNamespace); err != nil {
			return nil, nil, err
		}
		repo.FullName = target.PathWithNamespace
	} else {
		repo.Owner = req.FormValue("owner")
		repo.Name = req.FormValue("name")
		repo.FullName = fmt.Sprintf("%s/%s", repo.Owner, repo.Name)
	}

	repo.Link = target.WebURL

	if target.GitHTTPURL != "" {
		repo.Clone = target.GitHTTPURL
	} else {
		repo.Clone = target.HTTPURL
	}

	if target.DefaultBranch != "" {
		repo.Branch = target.DefaultBranch
	} else {
		repo.Branch = "master"
	}

	if target.AvatarURL != "" {
		repo.Avatar = target.AvatarURL
	}

	build.Event = model.EventPull

	lastCommit := obj.LastCommit

	build.Message = lastCommit.Message
	build.Commit = lastCommit.ID
	build.Remote = obj.Source.HTTPURL

	build.Ref = fmt.Sprintf("refs/merge-requests/%d/head", obj.IID)

	build.Branch = obj.SourceBranch

	author := lastCommit.Author

	build.Author = author.Name
	build.Email = author.Email

	if len(build.Email) != 0 {
		build.Avatar = getUserAvatar(build.Email)
	}

	build.Title = obj.Title
	build.Link = obj.URL

	return repo, build, nil
}

func convertPushHock(hook *gitlab.PushEvent) (*model.Repo, *model.Build, error) {
	repo := &model.Repo{}
	build := &model.Build{}

	var err error
	if repo.Owner, repo.Name, err = extractFromPath(hook.Project.PathWithNamespace); err != nil {
		return nil, nil, err
	}

	repo.Avatar = hook.Project.AvatarURL
	repo.Link = hook.Project.WebURL
	repo.Clone = hook.Project.GitHTTPURL
	repo.FullName = hook.Project.PathWithNamespace
	repo.Branch = hook.Project.DefaultBranch

	switch hook.Project.Visibility {
	case gitlab.PrivateVisibility:
		repo.IsPrivate = true
	case gitlab.InternalVisibility:
		repo.IsPrivate = true
	case gitlab.PublicVisibility:
		repo.IsPrivate = false
	}

	build.Event = model.EventPush
	build.Commit = hook.After
	build.Branch = strings.TrimPrefix(hook.Ref, "refs/heads/")
	build.Ref = hook.Ref

	for _, cm := range hook.Commits {
		if hook.After == cm.ID {
			build.Author = cm.Author.Name
			build.Email = cm.Author.Email
			build.Message = cm.Message
			build.Timestamp = cm.Timestamp.Unix()
			if len(build.Email) != 0 {
				build.Avatar = getUserAvatar(build.Email)
			}
			break
		}
	}

	return repo, build, nil
}

func convertTagHock(hook *gitlab.TagEvent) (*model.Repo, *model.Build, error) {
	repo := &model.Repo{}
	build := &model.Build{}

	var err error
	if repo.Owner, repo.Name, err = extractFromPath(hook.Project.PathWithNamespace); err != nil {
		return nil, nil, err
	}

	repo.Avatar = hook.Project.AvatarURL
	repo.Link = hook.Project.WebURL
	repo.Clone = hook.Project.GitHTTPURL
	repo.FullName = hook.Project.PathWithNamespace
	repo.Branch = hook.Project.DefaultBranch

	switch hook.Project.Visibility {
	case gitlab.PrivateVisibility:
		repo.IsPrivate = true
	case gitlab.InternalVisibility:
		repo.IsPrivate = true
	case gitlab.PublicVisibility:
		repo.IsPrivate = false
	}

	build.Event = model.EventTag
	build.Commit = hook.After
	build.Branch = strings.TrimPrefix(hook.Ref, "refs/heads/")
	build.Ref = hook.Ref

	for _, cm := range hook.Commits {
		if hook.After == cm.ID {
			build.Author = cm.Author.Name
			build.Email = cm.Author.Email
			build.Message = cm.Message
			build.Timestamp = cm.Timestamp.Unix()
			if len(build.Email) != 0 {
				build.Avatar = getUserAvatar(build.Email)
			}
			break
		}
	}

	return repo, build, nil
}

func getUserAvatar(email string) string {
	hasher := md5.New()
	hasher.Write([]byte(email))

	return fmt.Sprintf(
		"%s/%v.jpg?s=%s",
		gravatarBase,
		hex.EncodeToString(hasher.Sum(nil)),
		"128",
	)
}

func extractFromPath(str string) (string, string, error) {
	s := strings.Split(str, "/")
	if len(s) < 2 {
		return "", "", fmt.Errorf("Minimum match not found")
	}
	return s[0], s[1], nil
}
