package readapi

import (
	"encoding/json"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"testing"

	"gitlab.com/mbugroup/lti-api.git/internal/cache"
	"gitlab.com/mbugroup/lti-api.git/internal/config"
	"gitlab.com/mbugroup/lti-api.git/internal/route"

	"github.com/gofiber/fiber/v2"
	"github.com/redis/go-redis/v9"
	"gopkg.in/yaml.v3"
)

func TestGeneratedArtifactsAreCurrent(t *testing.T) {
	PrimeBuildConfig()
	cache.SetRedis(redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"}))
	app := fiber.New(config.FiberConfig())
	app.Get("/healthz", func(c *fiber.Ctx) error {
		return c.JSON(fiber.Map{"status": "ok"})
	})
	app.Get("/readyz", func(c *fiber.Ctx) error {
		return c.JSON(fiber.Map{"status": "ok"})
	})
	route.Routes(app, nil)

	artifacts, err := BuildArtifactsFromApp(app)
	if err != nil {
		t.Fatalf("build artifacts: %v", err)
	}

	root := repoRoot(t)
	assertJSONMatchesFile(t, artifacts.OpenAPIJSON, filepath.Join(root, "docs", "openapi", "read-api.json"))
	assertYAMLMatchesFile(t, artifacts.OpenAPIYAML, filepath.Join(root, "docs", "openapi", "read-api.yaml"))
	assertJSONMatchesFile(t, artifacts.PostmanCollectionJSON, filepath.Join(root, "docs", "postman", "read-api.collection.json"))
	assertJSONMatchesFile(t, artifacts.PostmanEnvironmentJSON, filepath.Join(root, "docs", "postman", "read-api.environment.json"))
}

func assertJSONMatchesFile(t *testing.T, got []byte, path string) {
	t.Helper()

	want, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("read %s: %v", path, err)
	}

	var gotValue any
	if err := json.Unmarshal(got, &gotValue); err != nil {
		t.Fatalf("unmarshal generated json: %v", err)
	}
	var wantValue any
	if err := json.Unmarshal(want, &wantValue); err != nil {
		t.Fatalf("unmarshal fixture json: %v", err)
	}

	if !reflect.DeepEqual(gotValue, wantValue) {
		t.Fatalf("json artifact mismatch for %s", path)
	}
}

func assertYAMLMatchesFile(t *testing.T, got []byte, path string) {
	t.Helper()

	want, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("read %s: %v", path, err)
	}

	var gotValue any
	if err := yaml.Unmarshal(got, &gotValue); err != nil {
		t.Fatalf("unmarshal generated yaml: %v", err)
	}
	var wantValue any
	if err := yaml.Unmarshal(want, &wantValue); err != nil {
		t.Fatalf("unmarshal fixture yaml: %v", err)
	}

	if !reflect.DeepEqual(gotValue, wantValue) {
		t.Fatalf("yaml artifact mismatch for %s", path)
	}
}

func repoRoot(t *testing.T) string {
	t.Helper()

	_, filename, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("runtime.Caller failed")
	}

	return filepath.Clean(filepath.Join(filepath.Dir(filename), "..", ".."))
}
