package webhook

import (
	"context"
	"fmt"
	"path"

	"github.com/alexei/docker-watcher/internal/store"
)

// FindProjectAndStage searches for a project whose image matches the parsed
// image reference, then finds the stage whose tag pattern matches the incoming
// tag. Returns (project, stage, found, error).
//
// Matching logic:
//  1. Iterate all projects.
//  2. Compare the project's Image field against the parsed image's FullName().
//  3. For the matched project, iterate its stages and find one whose TagPattern
//     matches the incoming tag using path.Match (glob semantics).
//  4. If multiple stages match, the first match wins (stages are ordered by name).
func FindProjectAndStage(ctx context.Context, st *store.Store, parsed ParsedImage) (store.Project, store.Stage, bool, error) {
	projects, err := st.GetAllProjects()
	if err != nil {
		return store.Project{}, store.Stage{}, false, fmt.Errorf("get projects: %w", err)
	}

	imageName := parsed.FullName()

	for _, project := range projects {
		if !imageMatches(project.Image, imageName) {
			continue
		}

		stage, found, err := matchStage(st, project.ID, parsed.Tag)
		if err != nil {
			return store.Project{}, store.Stage{}, false, fmt.Errorf("match stage for project %s: %w", project.Name, err)
		}
		if found {
			return project, stage, true, nil
		}

		// Project matches but no stage pattern matches this tag.
		// Return project with empty stage — caller can decide what to do.
		// For now, we treat it as "not found" so auto-create doesn't fire
		// for known projects with no matching stage.
		return store.Project{}, store.Stage{}, false, nil
	}

	return store.Project{}, store.Stage{}, false, nil
}

// imageMatches checks if a project's stored image name matches the parsed
// image name. The comparison is case-sensitive and supports the project image
// being stored as either "owner/name" or just "name".
func imageMatches(projectImage, incomingImage string) bool {
	if projectImage == incomingImage {
		return true
	}
	// Also match if the incoming image has an owner prefix but the project
	// only stores the bare name (or vice versa). This handles registries
	// that include or omit the owner segment.
	return false
}

// matchStage finds the first stage of a project whose tag pattern matches the
// given tag. Uses path.Match for glob-style matching (same as the registry poller).
func matchStage(st *store.Store, projectID, tag string) (store.Stage, bool, error) {
	stages, err := st.GetStagesByProjectID(projectID)
	if err != nil {
		return store.Stage{}, false, fmt.Errorf("get stages: %w", err)
	}

	for _, stage := range stages {
		pattern := stage.TagPattern
		if pattern == "" {
			pattern = "*"
		}

		matched, err := path.Match(pattern, tag)
		if err != nil {
			// Invalid pattern — skip this stage.
			continue
		}
		if matched {
			return stage, true, nil
		}
	}

	return store.Stage{}, false, nil
}
