package gitea

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/Nightapes/go-semantic-release/internal/assets"
	"github.com/Nightapes/go-semantic-release/internal/releaser/util"
	"github.com/Nightapes/go-semantic-release/internal/shared"
	"github.com/Nightapes/go-semantic-release/pkg/config"

	"code.gitea.io/sdk/gitea"
	log "github.com/sirupsen/logrus"
)

// GITEA identifer for gitea interface
const GITEA = "gitea"

// Client type struct
type GiteaClient struct {
	client  *gitea.Client
	config  *config.GiteaProvider
	context context.Context
	release *gitea.Release
	baseURL string
	log     *log.Entry
}

// New initialize a new GiteaRelease
func New(c *config.GiteaProvider, checkConfig bool) (*GiteaClient, error) {

	token, err := util.GetAccessToken("GITEA_TOKEN")
	if err != nil && checkConfig {
		return &GiteaClient{}, err
	}

	c.AccessToken = token

	if c.URL == "" {
		url, err := util.GetAccessToken("GITEA_URL")
		if err != nil && checkConfig {
			return &GiteaClient{}, err
		}
		c.URL = url
	}

	ctx := context.Background()
	//httpClient := util.CreateBearerHTTPClient(ctx, c.AccessToken)

	if c.Repo == "" && checkConfig {
		return nil, fmt.Errorf("gitea repo is not set")
	}

	if c.User == "" && checkConfig {
		return nil, fmt.Errorf("gitea user is not set")
	}

	if c.URL == "" && checkConfig {
		return nil, fmt.Errorf("gitea url is not set")
	}

	client, err := gitea.NewClient(c.URL,
		gitea.SetToken(c.AccessToken),
		// gitea.SetHTTPClient(p.HTTPClient()),
		gitea.SetContext(ctx))
	if err != nil {
		return &GiteaClient{}, err
	}

	return &GiteaClient{
		config:  c,
		client:  client,
		context: ctx,
		baseURL: c.URL,
		log:     log.WithField("releaser", GITEA),
	}, nil
}

// GetCommitURL for gitea
func (g *GiteaClient) GetCommitURL() string {
	return fmt.Sprintf("%s/%s/%s/commit/{{hash}}", g.baseURL, g.config.User, g.config.Repo)
}

// GetCompareURL for gitea
func (g *GiteaClient) GetCompareURL(oldVersion, newVersion string) string {
	return fmt.Sprintf("%s/%s/%s/compare/%s...%s", g.baseURL, g.config.User, g.config.Repo, oldVersion, newVersion)
}

// CreateRelease creates release on remote
func (g *GiteaClient) CreateRelease(releaseVersion *shared.ReleaseVersion, generatedChangelog *shared.GeneratedChangelog, assets *assets.Set) error {
	err := g.makeRelease(releaseVersion, generatedChangelog)
	if err != nil {
		return err
	}
	return g.uploadAssets(assets)
}

// CreateRelease creates release on remote
func (g *GiteaClient) makeRelease(releaseVersion *shared.ReleaseVersion, generatedChangelog *shared.GeneratedChangelog) error {

	tagPrefix := config.DefaultTagPrefix
	if g.config.TagPrefix != nil {
		tagPrefix = *g.config.TagPrefix
	}
	tag := tagPrefix + releaseVersion.Next.Version.String()
	g.log.Debugf("create release with version %s", tag)

	prerelease := releaseVersion.Next.Version.Prerelease() != ""

	opt := gitea.CreateReleaseOption{TagName: tag,
		Target:       releaseVersion.Branch,
		Title:        generatedChangelog.Title,
		Note:         generatedChangelog.Content,
		IsPrerelease: prerelease}
	// TODO Test if this prevents release double-up
	release, _, err := g.client.CreateRelease(g.config.User, g.config.Repo, opt)

	g.log.Debugf("Release response: %+v", *release)

	if err != nil {
		g.log.Debugf("Release Error response: %+v", err)
		if strings.Contains(err.Error(), "Release is has no Tag") {
			g.log.Infof("A release with tag %s already exits, will not perform a release or update", tag)
			return nil
		}
		return fmt.Errorf("could not create release: %s", err.Error())
	}
	g.release = release
	g.log.Infof("Created release")
	return nil

}

// UploadAssets uploads specified assets
func (g *GiteaClient) uploadAssets(assets *assets.Set) error {
	if g.release != nil {
		for _, asset := range assets.All() {
			path, err := asset.GetPath()
			if err != nil {
				return err
			}
			file, err := os.Open(path)
			if err != nil {
				return err
			}

			_, resp, err := g.client.CreateReleaseAttachment(g.config.User, g.config.Repo, g.release.ID, file, asset.GetName())
			if err != nil {
				return err
			}

			if resp.StatusCode >= http.StatusBadRequest {
				return fmt.Errorf("could not upload asset %s: %s", asset.GetName(), resp.Status)
			}
		}
	}
	return nil
}
