// SPDX-FileCopyrightText: 2023 Luke Granger-Brown <depot@lukegb.com>
//
// SPDX-License-Identifier: Apache-2.0

package nixbuild

import (
	"context"
	"io"
	"strings"

	"hg.lukegb.com/lukegb/depot/go/nix/nar/narinfo"
	"hg.lukegb.com/lukegb/depot/go/nix/nixdrv"
	"hg.lukegb.com/lukegb/depot/go/nix/nixpool"
)

type Fetcher interface {
	FetchNarInfo(ctx context.Context, path string) (*narinfo.NarInfo, error)
	FetchNar(ctx context.Context, ni *narinfo.NarInfo) (io.ReadCloser, error)
}

type DerivationFetcher interface {
	nixdrv.Resolver
	FetchDerivationForPath(ctx context.Context, path string) (*nixdrv.BasicDerivation, string, error)
}

type Resolver interface {
	Fetcher
	RelativePriority() int
}

type Target interface {
	ValidPaths(ctx context.Context, paths []string) ([]string, error)
	EnsurePaths(ctx context.Context, paths []string) ([]string, error)
	PutNar(ctx context.Context, ni *narinfo.NarInfo, w io.Reader) error
	Fetcher
}

type Builder struct {
	Pool               *nixpool.Pool
	SupportedPlatforms map[string]bool
	SupportedFeatures  map[string]bool
	RequiredFeatures   []map[string]bool
}

func (b *Builder) SupportsAll(features map[string]bool) bool {
	for f := range features {
		if b.SupportedFeatures == nil || !b.SupportedFeatures[f] {
			return false
		}
	}
	return true
}

func (b *Builder) CanBuild(features map[string]bool) bool {
	if len(b.RequiredFeatures) == 0 {
		return true
	}
	if features == nil {
		return false
	}
requiredFeaturesLoop:
	for _, rf := range b.RequiredFeatures {
		for f := range rf {
			if !features[f] {
				continue requiredFeaturesLoop
			}
		}
		return true
	}
	return false
}

type Config struct {
	// Target is the endpoint which should end up having all the built packages.
	// If Target is nil, we will build things if necessary and leave them on the builders. Things may end up in a weird state if Target is nil and the Builders are not present in Resolvers, since built dependencies may not be available.
	Target Target

	// Allow target to resolve packages on its own, from its locally configured caches?
	ResolveOnTarget bool

	// Things to resolve packages from.
	// You should probably put Target and Builders in this list.
	Resolvers []Resolver

	// Peers to build packages on.
	// If empty, building will be disabled.
	Builders []*Builder

	// Allow builders to resolve dependencies on their own, from their locally configured caches?
	ResolveDependenciesOnBuilders bool

	// Store path. Usually /nix/store, which is the default if empty.
	StorePath string
}

func (c Config) storePath() string {
	if c.StorePath == "" {
		return "/nix/store"
	}
	return strings.TrimSuffix(c.StorePath, "/")
}