package plugin

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"regexp"
	"text/template"

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

var (
	defaultTitleFormat = "Release {{.Tag}}"
	defaultTagRegex    = regexp.MustCompile(`((?P<version>[\d]+\.[\d]+\.[\w-]+))`)
)

// Args provides plugin execution arguments.
type Args struct {
	Pipeline

	// Level defines the plugin log level.
	Level string `envconfig:"PLUGIN_LOG_LEVEL"`

	GiteaUrl          string `envconfig:"PLUGIN_GITEA_URL"`
	GiteaUsername     string `envconfig:"PLUGIN_GITEA_USERNAME"`
	GiteaPassword     string `envconfig:"PLUGIN_GITEA_PASSWORD"`
	GitUsername       string `envconfig:"PLUGIN_GIT_USERNAME"`
	GitPassword       string `envconfig:"PLUGIN_GIT_PASSWORD"`
	Owner             string `envconfig:"PLUGIN_OWNER"`
	Repo              string `envconfig:"PLUGIN_REPO"`
	TitleFormat       string `envconfig:"PLUGIN_TITLE_FORMAT"`
	UseLatestGitTag   bool   `envconfig:"PLUGIN_USE_LATEST_GIT_TAG" default:"true"`
	TagFile           string `envconfig:"PLUGIN_TAG_FILE"`
	TagRegex          string `envconfig:"PLUGIN_TAG_REGEX"`
	NotesFile         string `envconfig:"PLUGIN_NOTES_FILE"`
	Notes             string `envconfig:"PLUGIN_NOTES"`
	IsPrerelease      bool   `envconfig:"PLUGIN_IS_PRERELEASE"`
	SkipIfNoNotesFile bool   `envconfig:"PLUGIN_SKIP_IF_NO_NOTES_FILE"`
	FetchGitTags      bool   `envconfig:"PLUGIN_FETCH_GIT_TAGS" default:"true"`
}

type ReleaseCard struct {
	Title string
	Tag   string
	Url   string
}

type TitleTemplateCtx struct {
	Tag          string
	GiteaUrl     string
	Owner        string
	Repo         string
	IsPrerelease bool
}

// Exec executes the plugin.
func Exec(ctx context.Context, args Args) error {
	var (
		note  string
		tag   string
		title string
		err   error
	)

	if args.Notes != "" {
		note = args.Notes
	} else if args.NotesFile != "" {
		content, err := os.ReadFile(args.NotesFile)

		if err != nil {
			if os.IsNotExist(err) && args.SkipIfNoNotesFile {
				log.Info("No notes file found, skipping release")
				return nil
			}
			return fmt.Errorf("error reading notes file %w", err)
		}

		note = string(content)
	} else {
		return fmt.Errorf("notes or notes file must be specified")
	}

	if args.UseLatestGitTag {
		var options = make([]GetLatestGitTagOption, 0)
		if args.GitUsername != "" && args.GitPassword != "" {
			options = append(options, SetBasicAuth(args.GitUsername, args.GitPassword))
		}
		if args.FetchGitTags {
			options = append(options, FetchTags())
		}
		tag, err = getLatestGitTag(options...)

		if err != nil {
			return fmt.Errorf("error getting git tag %w", err)
		}

		log.WithField("tag", tag).Info("Fetched latest git tag")
	} else if args.TagFile != "" {
		log.WithField("file", args.TagFile).Info("Reading tag from file")

		var pattern = defaultTagRegex
		if args.TagRegex != "" {
			pattern = regexp.MustCompile(args.TagRegex)
		}

		content, err := os.ReadFile(args.TagFile)

		if err != nil {
			return fmt.Errorf("error reading tag file %w", err)
		}

		matches := pattern.FindStringSubmatch(string(content))

		if len(matches) == 0 {
			return fmt.Errorf("no matches found in tag file")
		}

		for i, name := range pattern.SubexpNames() {
			if name == "version" {
				tag = matches[i]
			}
		}

		log.WithField("tag", tag).Info("Found tag")
	} else {
		return fmt.Errorf("latest git tag or tag file must be given")
	}

	var titleTmpl *template.Template
	if args.TitleFormat != "" {
		titleTmpl, err = template.New("title").Parse(args.TitleFormat)
	} else {
		titleTmpl, err = template.New("title").Parse(defaultTitleFormat)
	}

	if err != nil {
		return fmt.Errorf("error reading template %w", err)
	}

	var titleBytes []byte
	var titleBuffer = bytes.NewBuffer(titleBytes)

	if err = titleTmpl.Execute(titleBuffer, TitleTemplateCtx{Tag: tag, GiteaUrl: args.GiteaUrl, Owner: args.Owner, Repo: args.Repo}); err != nil {
		return fmt.Errorf("error reading template %w", err)
	}

	title = titleBuffer.String()

	log.WithFields(log.Fields{
		"template": titleTmpl.DefinedTemplates(),
		"title":    title,
	}).Info("Generated title with template")

	client, err := gitea.NewClient(args.GiteaUrl, gitea.SetBasicAuth(args.GiteaUsername, args.GiteaPassword))

	if err != nil {
		return fmt.Errorf("error creating Gitea client %w", err)
	}

	release, _, err := client.CreateRelease(args.Owner, args.Repo, gitea.CreateReleaseOption{
		TagName:      tag,
		Title:        title,
		Note:         note,
		IsPrerelease: args.IsPrerelease,
	})

	if err != nil {
		return fmt.Errorf("error creating Gitea release %w", err)
	}

	releaseURL := fmt.Sprintf("%s/%s/%s/releases/tag/%s", args.GiteaUrl, args.Owner, args.Repo, release.TagName)
	fmt.Printf("Successfully created release at %s", releaseURL)

	writeCard(
		args.Pipeline.Card.Path,
		"https://gitea.dikurium.ch/InnoPeak/drone-gitea-release/raw/branch/main/card.json",
		ReleaseCard{
			Title: title,
			Tag:   tag,
			Url:   releaseURL,
		},
	)

	return err
}