// Command dev-stub is a throwaway fake git.sr.ht GraphQL API for local
// development of compare.sr.ht. It answers the two queries compare.sr.ht's
// authorizer issues (a single `user{repository}` lookup and the `me`
// repository list) with fixed PUBLIC data, so you can drive the UI without a
// real SourceHut instance or its internal-auth machinery.
//
// It ignores authentication entirely: every request — anonymous or "logged in"
// — sees the same public repository, which is exactly what you want when
// exercising the compare/commit pages against a directory of local bare repos.
//
// Usage:
//
//	go run ./contrib/dev-stub -addr 127.0.0.1:5101
//
// Then point compare.sr.ht's config at it and start the daemon:
//
//	[git.sr.ht]
//	api-origin=http://127.0.0.1:5101   # dev-stub serves POST /query here
//	repos=/path/to/local/bare/repos    # {repos}/~{owner}/{name}
//
//	make run-dev
//
// The repository NAME echoed back is taken from the query variables, so any
// /~owner/<name> you visit resolves; put a matching bare repo at
// {repos}/~owner/<name> for gitx to read. See README.md ("Development") for the
// full recipe including forging a dev login cookie.
//
// stdlib only, no build-time dependencies. Not meant for production.
package main

import (
	"encoding/json"
	"flag"
	"log"
	"net/http"
	"strings"
)

// repoNode is the repository shape compare.sr.ht's authorizer decodes for both
// the single-repo lookup and the me.repositories list.
type repoNode struct {
	ID          int    `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	Visibility  string `json:"visibility"`
}

type gqlRequest struct {
	Query     string         `json:"query"`
	Variables map[string]any `json:"variables"`
}

func main() {
	addr := flag.String("addr", "127.0.0.1:5101", "address to listen on")
	flag.Parse()

	http.HandleFunc("/query", handleQuery)
	http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte("ok\n"))
	})

	log.Printf("dev-stub git.sr.ht GraphQL API listening on http://%s/query", *addr)
	log.Printf("every query resolves to a fixed PUBLIC repository (auth ignored)")
	if err := http.ListenAndServe(*addr, nil); err != nil {
		log.Fatal(err)
	}
}

func handleQuery(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "POST only", http.StatusMethodNotAllowed)
		return
	}
	var req gqlRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeData(w, map[string]any{}) // let the client surface a decode-shaped miss
		return
	}

	switch {
	case strings.Contains(req.Query, "repositories("):
		// me { repositories { results { ... } cursor } }
		writeData(w, map[string]any{
			"me": map[string]any{
				"repositories": map[string]any{
					"results": []repoNode{
						{ID: 1, Name: "demo", Description: "a public demo repo", Visibility: "PUBLIC"},
						{ID: 2, Name: "playground", Description: "scratch space", Visibility: "UNLISTED"},
					},
					"cursor": nil,
				},
			},
		})
	case strings.Contains(req.Query, "repository("):
		// user(username:$u) { repository(name:$r) { ... } }
		name, _ := req.Variables["r"].(string)
		if name == "" {
			name = "demo"
		}
		writeData(w, map[string]any{
			"user": map[string]any{
				"repository": repoNode{
					ID:          1,
					Name:        name,
					Description: "a public demo repo (dev-stub)",
					Visibility:  "PUBLIC",
				},
			},
		})
	default:
		writeData(w, map[string]any{})
	}
}

func writeData(w http.ResponseWriter, data map[string]any) {
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{"data": data})
}
