package oci

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"

	"git.wntrmute.dev/kyle/mcr/internal/db"
	"git.wntrmute.dev/kyle/mcr/internal/policy"
)

type tagListResponse struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

func (h *Handler) handleTagsList(w http.ResponseWriter, r *http.Request, repo string) {
	if !h.checkPolicy(w, r, policy.ActionPull, repo) {
		return
	}

	repoID, err := h.db.GetRepositoryByName(repo)
	if err != nil {
		if errors.Is(err, db.ErrRepoNotFound) {
			writeOCIError(w, "NAME_UNKNOWN", http.StatusNotFound,
				fmt.Sprintf("repository %q not found", repo))
			return
		}
		writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
		return
	}

	// Parse pagination params.
	n := 0
	if nStr := r.URL.Query().Get("n"); nStr != "" {
		n, err = strconv.Atoi(nStr)
		if err != nil || n < 0 {
			writeOCIError(w, "INVALID_PARAMETER", http.StatusBadRequest, "invalid 'n' parameter")
			return
		}
	}
	last := r.URL.Query().Get("last")

	// Default: no limit (return all).
	if n == 0 {
		tags, err := h.db.ListTags(repoID, last, 10000)
		if err != nil {
			writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
			return
		}
		if tags == nil {
			tags = []string{}
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(tagListResponse{Name: repo, Tags: tags})
		return
	}

	// Request n+1 to detect if there are more results.
	tags, err := h.db.ListTags(repoID, last, n+1)
	if err != nil {
		writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
		return
	}
	if tags == nil {
		tags = []string{}
	}

	hasMore := len(tags) > n
	if hasMore {
		tags = tags[:n]
	}

	if hasMore {
		lastTag := tags[len(tags)-1]
		linkURL := fmt.Sprintf("/v2/%s/tags/list?n=%d&last=%s", repo, n, lastTag)
		w.Header().Set("Link", fmt.Sprintf(`<%s>; rel="next"`, linkURL))
	}

	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(tagListResponse{Name: repo, Tags: tags})
}
