package handler

import (
	"context"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"codeberg.org/VARASYS/ZDDC/zddc/internal/config"
	"codeberg.org/VARASYS/ZDDC/zddc/internal/zddc"
)

func TestIsZddcFileRequest(t *testing.T) {
	cases := []struct {
		url  string
		want bool
	}{
		{"/.zddc", true},
		{"/.zddc/", true},
		{"/Project/.zddc", true},
		{"/Project/archive/PartyA/mdl/.zddc", true},
		{"/.zddc.html", false}, // editor leaf, handled separately
		{"/Project/.zddc.html", false},
		{"/Project/.zddc/", true},
		{"/Project/", false},
		{"/", false},
	}
	for _, tc := range cases {
		if got := IsZddcFileRequest(tc.url); got != tc.want {
			t.Errorf("IsZddcFileRequest(%q) = %v, want %v", tc.url, got, tc.want)
		}
	}
}

func TestServeZddcFile_ExistingFile(t *testing.T) {
	root := t.TempDir()
	mustWrite(t, filepath.Join(root, ".zddc"),
		"title: root\nacl:\n  permissions:\n    \"*\": rwcda\n")
	subDir := filepath.Join(root, "Project")
	if err := os.Mkdir(subDir, 0o755); err != nil {
		t.Fatal(err)
	}
	mustWrite(t, filepath.Join(subDir, ".zddc"),
		"title: project-level\n")

	zddc.InvalidateCache(root)
	cfg := config.Config{Root: root, EmailHeader: "X-Auth-Request-Email"}

	req := httptest.NewRequest(http.MethodGet, "/Project/.zddc", nil)
	req = req.WithContext(context.WithValue(req.Context(), EmailKey, "alice@example.com"))
	rec := httptest.NewRecorder()
	ServeZddcFile(cfg, rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("status = %d, body = %s", rec.Code, rec.Body.String())
	}
	if !strings.Contains(rec.Body.String(), "title: project-level") {
		t.Errorf("body missing on-disk content: %q", rec.Body.String())
	}
	if got := rec.Header().Get("X-ZDDC-Source"); !strings.HasPrefix(got, "file:") {
		t.Errorf("X-ZDDC-Source = %q, want file:* prefix", got)
	}
	if got := rec.Header().Get("Content-Type"); !strings.HasPrefix(got, "application/yaml") {
		t.Errorf("Content-Type = %q, want application/yaml*", got)
	}
}

func TestServeZddcFile_VirtualDefault(t *testing.T) {
	root := t.TempDir()
	mustWrite(t, filepath.Join(root, ".zddc"),
		"title: bootstrap\nacl:\n  permissions:\n    \"*\": rwcda\n")
	// Directory exists but has no .zddc.
	subDir := filepath.Join(root, "Project")
	if err := os.Mkdir(subDir, 0o755); err != nil {
		t.Fatal(err)
	}
	zddc.InvalidateCache(root)
	cfg := config.Config{Root: root, EmailHeader: "X-Auth-Request-Email"}

	req := httptest.NewRequest(http.MethodGet, "/Project/.zddc", nil)
	req = req.WithContext(context.WithValue(req.Context(), EmailKey, "alice@example.com"))
	rec := httptest.NewRecorder()
	ServeZddcFile(cfg, rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("status = %d, body = %s", rec.Code, rec.Body.String())
	}
	if got := rec.Header().Get("X-ZDDC-Source"); got != "virtual:zddc" {
		t.Errorf("X-ZDDC-Source = %q, want virtual:zddc", got)
	}
	body := rec.Body.String()
	if !strings.Contains(body, "Virtual .zddc") {
		t.Errorf("body missing virtual marker: %q", body)
	}
	// Should show the root's title from the cascade.
	if !strings.Contains(body, "bootstrap") {
		t.Errorf("body missing root cascade summary: %q", body)
	}
	// Should parse as valid YAML (empty document or {} at the end).
	if !strings.Contains(body, "{}") {
		t.Errorf("body missing placeholder body: %q", body)
	}
}

func TestServeZddcFile_NonGetRejected(t *testing.T) {
	root := t.TempDir()
	mustWrite(t, filepath.Join(root, ".zddc"),
		"acl:\n  permissions:\n    \"*\": rwcda\n")
	zddc.InvalidateCache(root)
	cfg := config.Config{Root: root, EmailHeader: "X-Auth-Request-Email"}

	req := httptest.NewRequest(http.MethodPut, "/.zddc",
		strings.NewReader("title: hacked\n"))
	req = req.WithContext(context.WithValue(req.Context(), EmailKey, "alice@example.com"))
	rec := httptest.NewRecorder()
	ServeZddcFile(cfg, rec, req)

	if rec.Code != http.StatusMethodNotAllowed {
		t.Errorf("status = %d, want 405", rec.Code)
	}
}
