// Copyright 2019 Drone IO, Inc.
//
// 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 commit

import (
	"context"

	"github.com/drone/drone/core"
	"github.com/drone/go-scm/scm"
)

// New returns a new CommitServiceFactory.
func New(client *scm.Client, renew core.Renewer) core.CommitService {
	return &service{
		client: client,
		renew:  renew,
	}
}

type service struct {
	renew  core.Renewer
	client *scm.Client
}

func (s *service) Find(ctx context.Context, user *core.User, repo, sha string) (*core.Commit, error) {
	err := s.renew.Renew(ctx, user, false)
	if err != nil {
		return nil, err
	}
	ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{
		Token:   user.Token,
		Refresh: user.Refresh,
	})
	commit, _, err := s.client.Git.FindCommit(ctx, repo, sha)
	if err != nil {
		return nil, err
	}
	return &core.Commit{
		Sha:     commit.Sha,
		Message: commit.Message,
		Link:    commit.Link,
		Author: &core.Committer{
			Name:   commit.Author.Name,
			Email:  commit.Author.Email,
			Date:   commit.Author.Date.Unix(),
			Login:  commit.Author.Login,
			Avatar: commit.Author.Avatar,
		},
		Committer: &core.Committer{
			Name:   commit.Committer.Name,
			Email:  commit.Committer.Email,
			Date:   commit.Committer.Date.Unix(),
			Login:  commit.Committer.Login,
			Avatar: commit.Committer.Avatar,
		},
	}, nil
}

func (s *service) FindRef(ctx context.Context, user *core.User, repo, ref string) (*core.Commit, error) {
	err := s.renew.Renew(ctx, user, false)
	if err != nil {
		return nil, err
	}
	ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{
		Token:   user.Token,
		Refresh: user.Refresh,
	})

	switch s.client.Driver {
	case scm.DriverBitbucket,
		scm.DriverStash:
		ref = scm.TrimRef(ref)
		branch, _, err := s.client.Git.FindBranch(ctx, repo, ref) // wont work for a Tag
		if err != nil {
			return nil, err
		}
		ref = branch.Sha
	}

	commit, _, err := s.client.Git.FindCommit(ctx, repo, ref)
	if err != nil {
		return nil, err
	}
	return &core.Commit{
		Sha:     commit.Sha,
		Ref:     ref,
		Message: commit.Message,
		Link:    commit.Link,
		Author: &core.Committer{
			Name:   commit.Author.Name,
			Email:  commit.Author.Email,
			Date:   commit.Author.Date.Unix(),
			Login:  commit.Author.Login,
			Avatar: commit.Author.Avatar,
		},
		Committer: &core.Committer{
			Name:   commit.Committer.Name,
			Email:  commit.Committer.Email,
			Date:   commit.Committer.Date.Unix(),
			Login:  commit.Committer.Login,
			Avatar: commit.Committer.Avatar,
		},
	}, nil
}

func (s *service) ListChanges(ctx context.Context, user *core.User, repo, sha, ref string) ([]*core.Change, error) {
	err := s.renew.Renew(ctx, user, false)
	if err != nil {
		return nil, err
	}
	ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{
		Token:   user.Token,
		Refresh: user.Refresh,
	})
	out, _, err := s.client.Git.ListChanges(ctx, repo, sha, scm.ListOptions{Size: 100})
	if err != nil {
		return nil, err
	}
	var changes []*core.Change
	for _, change := range out {
		changes = append(changes, &core.Change{
			Path:    change.Path,
			Added:   change.Added,
			Renamed: change.Renamed,
			Deleted: change.Deleted,
		})
	}
	return changes, nil
}
