package registry

import (
	"context"
	"fmt"
	"path"
	"sort"
	"strings"
)

// RegistryImage represents a container image discovered from a registry.
type RegistryImage struct {
	Name    string `json:"name"`
	Owner   string `json:"owner"`
	FullRef string `json:"full_ref"` // e.g., "git.example.com/owner/my-app"
}

// Client defines the interface for interacting with a container image registry.
type Client interface {
	// ListTags returns all available tags for the given image.
	ListTags(ctx context.Context, image string) ([]string, error)

	// GetLatestTag returns the most recently created tag that matches the given
	// glob pattern. Returns an empty string and no error if no tags match.
	GetLatestTag(ctx context.Context, image string, pattern string) (string, error)

	// ListImages returns all container images available in the registry for the
	// given owner. Returns an error if the registry does not support image listing.
	ListImages(ctx context.Context, owner string) ([]RegistryImage, error)
}

// DeployTriggerer is called by the poller when a new tag is detected for a
// stage with auto_deploy enabled. This decouples the registry package from the
// deployer implementation.
type DeployTriggerer interface {
	TriggerDeploy(ctx context.Context, projectID, stageID, imageTag string) error
}

// MatchTags filters a list of tags, returning only those that match the given
// glob pattern. Pattern matching uses path.Match semantics (*, ?, []).
// Returns an error if the pattern is malformed.
func MatchTags(tags []string, pattern string) ([]string, error) {
	if pattern == "" || pattern == "*" {
		result := make([]string, len(tags))
		copy(result, tags)
		return result, nil
	}

	// Validate pattern once before iterating.
	if _, err := path.Match(pattern, ""); err != nil {
		return nil, fmt.Errorf("invalid tag pattern %q: %w", pattern, err)
	}

	var matched []string
	for _, tag := range tags {
		ok, _ := path.Match(pattern, tag)
		if ok {
			matched = append(matched, tag)
		}
	}
	return matched, nil
}

// LatestTag returns the last element of a sorted tag list that matches the
// pattern. Tags are sorted lexicographically; the "latest" is the last in sort
// order. Returns empty string if no tags match. Returns an error if the pattern
// is malformed.
func LatestTag(tags []string, pattern string) (string, error) {
	matched, err := MatchTags(tags, pattern)
	if err != nil {
		return "", err
	}
	if len(matched) == 0 {
		return "", nil
	}
	sort.Strings(matched)
	return matched[len(matched)-1], nil
}

// NewClient creates a registry Client based on the registry type string.
// Supported types: "gitea". Future: "github", "dockerhub".
func NewClient(registryType, baseURL, token string) (Client, error) {
	switch strings.ToLower(registryType) {
	case "gitea":
		return NewGiteaClient(baseURL, token), nil
	default:
		return nil, fmt.Errorf("unsupported registry type: %s", registryType)
	}
}
