package rosa

import (
	"io/fs"
	"os"
	"path"
	"slices"
	"strconv"
	"strings"
	"time"

	"hakurei.app/internal/pkg"
)

const (
	// jobsE is expression for preferred job count set by [pkg].
	jobsE = `"$` + pkg.EnvJobs + `"`
	// jobsFlagE is expression for flag with preferred job count.
	jobsFlagE = `"-j$` + pkg.EnvJobs + `"`
	// jobsLE is expression for twice of preferred job count set by [pkg].
	jobsLE = `"$(expr ` + jobsE + ` '*' 2)"`
	// jobsLFlagE is expression for flag with double of preferred job count.
	jobsLFlagE = `"-j$(expr ` + jobsE + ` '*' 2)"`

	// loadE is expression for preferred loadavg target set by [pkg].
	loadE = `"$` + pkg.EnvLoad + `"`
	// loadFlagE is expression for flag with preferred loadavg target.
	loadFlagE = `"-l$` + pkg.EnvLoad + `"`
)

// newTar is a helper for downloading compressed tarballs.
func newTar(url, checksum string, compress uint32) pkg.Artifact {
	return pkg.NewTar(
		pkg.NewDecompress(
			pkg.NewHTTPGet(nil, url, mustDecode(checksum)),
			compress,
		),
	)
}

// newFromCPAN is a helper for downloading release from CPAN.
func newFromCPAN(author, name, version, checksum string) pkg.Artifact {
	return newTar(
		"https://cpan.metacpan.org/authors/id/"+
			author[:1]+"/"+author[:2]+"/"+author+"/"+
			name+"-"+version+".tar.gz",
		checksum,
		pkg.Gzip,
	)
}

// newFromGitLab is a helper for downloading source from GitLab.
func newFromGitLab(domain, suffix, ref, checksum string) pkg.Artifact {
	return newTar(
		"https://"+domain+"/"+suffix+"/-/archive/"+
			ref+"/"+path.Base(suffix)+"-"+
			strings.ReplaceAll(ref, "/", "-")+".tar.bz2",
		checksum,
		pkg.Bzip2,
	)
}

// newFromGitHub is a helper for downloading source from Microsoft Github.
func newFromGitHub(suffix, tag, checksum string) pkg.Artifact {
	return newTar(
		"https://github.com/"+suffix+
			"/archive/refs/tags/"+tag+".tar.gz",
		checksum,
		pkg.Gzip,
	)
}

// newFromGitHubRelease is a helper for downloading release tarball from
// Microsoft Github.
func newFromGitHubRelease(
	suffix, tag, name, checksum string,
	compression uint32,
) pkg.Artifact {
	return newTar(
		"https://github.com/"+suffix+
			"/releases/download/"+tag+"/"+name,
		checksum,
		compression,
	)
}

// skipGNUTests generates a string for skipping specific tests by number in a
// GNU test suite. This is nontrivial because the test suite does not support
// excluding tests in any way, so ranges for all but the skipped tests have to
// be specified instead.
//
// For example, to skip test 764, ranges around the skipped test must be
// specified:
//
//	1-763 765-
//
// Tests are numbered starting from 1. The resulting string is unquoted.
func skipGNUTests(tests ...int64) string {
	tests = slices.Clone(tests)
	slices.Sort(tests)

	var buf strings.Builder

	if tests[0] != 1 {
		buf.WriteString("1-")
	}

	for i, n := range tests {
		if n != 1 && (i == 0 || tests[i-1] != n-1) {
			buf.WriteString(strconv.Itoa(int(n - 1)))
			buf.WriteString(" ")
		}
		if i == len(tests)-1 || (tests[i+1] != n+1 && tests[i+1] != n+2) {
			buf.WriteString(strconv.Itoa(int(n + 1)))
			buf.WriteString("-")
		}
	}
	return buf.String()
}

var (
	// builtin contains native and embedded [Artifact] registrations.
	builtin S

	// parseTime is the duration of early parsing of built-in azalea expressions.
	parseTime time.Duration
)

// ParseTime returns the time taken by early parsing of built-in azalea expressions.
func ParseTime() time.Duration { return parseTime }

func init() {
	sub, err := fs.Sub(builtinAzalea, "package")
	if err != nil {
		panic(err)
	}
	t := time.Now()
	if err = builtin.RegisterFS(sub); err != nil {
		println(err.Error())
		os.Exit(1)
	}
	parseTime = time.Since(t)
}

// New returns the address of a newly populated [S] derived from built-ins.
func New() *S { return builtin.Clone() }

// Collect returns all built-in non-excluded [ArtifactH].
func Collect() (handles P) { return builtin.Collect() }

// CollectAll returns all built-in [ArtifactH].
func CollectAll() (handles P) { return builtin.CollectAll() }

// builtinStd is the [Std] toolchain stage on builtin.
var builtinStd = builtin.Std()

// Load satisfies an [Artifact] referred to by an [ArtifactH].
func Load(handle ArtifactH) (*Metadata, pkg.Artifact) {
	return builtinStd.Load(handle)
}

// LoadAt satisfies an [Artifact] referred to by an [ArtifactH] at the
// specified stage.
func LoadAt(stage Stage, handle ArtifactH) (*Metadata, pkg.Artifact) {
	return builtin.New(stage).Load(handle)
}

// MustLoad is like Load, but panics if the named [Artifact] is not registered.
func MustLoad(handle ArtifactH) (*Metadata, pkg.Artifact) {
	return builtinStd.MustLoad(handle)
}

// MustLoadAt is like LoadAt, but panics if the named [Artifact] is not
// registered.
func MustLoadAt(stage Stage, handle ArtifactH) (*Metadata, pkg.Artifact) {
	return builtin.New(stage).MustLoad(handle)
}

// HasStageEarly returns whether a stage0 distribution is available.
func HasStageEarly() (ok bool) { return builtin.HasStageEarly() }
