package repo

import (
	"errors"
	"strings"
	"time"

	"git.polynom.me/rio/internal/dns"
	"git.polynom.me/rio/internal/pages"

	"code.gitea.io/sdk/gitea"
	"github.com/patrickmn/go-cache"
	log "github.com/sirupsen/logrus"
)

var (
	pathCache = cache.New(1*time.Hour, 1*time.Hour)
)

type PageCacheEntry struct {
	Repository *gitea.Repository
	Path       string
}

func makePageCacheKey(domain, path string) string {
	return domain + "/" + path
}

// / Try to find the repository with name @reponame of the user @username. If @cname
// / is not "", then it also verifies that the repository contains a "CNAME" with
// / the value of @cname as its content. @host, @domain, and @path are passed for
// / caching on success.
func lookupRepositoryAndCache(username, reponame, host, domain, path, cname string, giteaClient *gitea.Client) (*gitea.Repository, error) {
	log.Debugf("Looking up repository %s/%s", username, reponame)
	repo, _, err := giteaClient.GetRepo(username, reponame)
	if err != nil {
		return nil, err
	}

	// Check if the CNAME file matches
	if cname != "" {
		file, _, err := giteaClient.GetFile(
			username,
			repo.Name,
			pages.PagesBranch,
			"CNAME",
			false,
		)
		if err != nil {
			log.Errorf("Could not verify CNAME of %s/%s: %v\n", username, repo.Name, err)
			return nil, err
		}

		cnameContent := strings.Trim(
			string(file[:]),
			"\n",
		)
		if cnameContent != cname {
			return nil, errors.New("CNAME mismatch")
		}
	}

	// Cache data
	pathCache.Set(
		makePageCacheKey(domain, path),
		PageCacheEntry{
			repo,
			path,
		},
		cache.DefaultExpiration,
	)
	return repo, nil
}

func RepoFromPath(username, host, cname, path string, giteaClient *gitea.Client) (*gitea.Repository, string, error) {
	domain := host

	// Guess the repository
	key := makePageCacheKey(domain, path)
	entry, found := pathCache.Get(key)
	if found {
		pageEntry := entry.(PageCacheEntry)
		return pageEntry.Repository, pageEntry.Path, nil
	}

	pathParts := strings.Split(path, "/")
	if len(pathParts) > 1 {
		log.Debugf("Trying repository %s", pathParts[0])
		modifiedPath := strings.Join(pathParts[1:], "/")
		repo, err := lookupRepositoryAndCache(
			username,
			pathParts[0],
			host,
			domain,
			modifiedPath,
			cname,
			giteaClient,
		)
		if err == nil {
			return repo, modifiedPath, nil
		}
	}

	// Allow specifying the repository name in the TXT record
	reponame := domain
	lookupDomain := domain
	if cname != "" {
		lookupDomain = cname
	}
	repoLookup, err := dns.LookupRepoTXT(lookupDomain)
	if err != nil && repoLookup != "" {
		log.Infof(
			"TXT lookup for %s resulted in choosing repository %s",
			lookupDomain,
			repoLookup,
		)
		reponame = repoLookup
	} else if cname != "" {
		// Allow naming the repository "example.org" (But give the TXT record preference)
		reponame = cname
	}

	log.Debugf("Trying repository %s/%s", username, reponame)
	repo, err := lookupRepositoryAndCache(
		username,
		reponame,
		host,
		domain,
		path,
		cname,
		giteaClient,
	)
	return repo, path, err
}
