package web

import (
	"database/sql"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/AmanTahiliani/box-box/internal/api"
	_ "modernc.org/sqlite"
)

type fakeStaleReporter bool

func (f fakeStaleReporter) LastResponseWasStale() bool { return bool(f) }

func TestStaleFreshnessTakesPrecedenceOverPartialAndLimited(t *testing.T) {
	for _, fallback := range []string{"partial", "limited"} {
		recorder := httptest.NewRecorder()
		markOpenF1Availability(recorder, fakeStaleReporter(true), fallback)
		if recorder.Header().Get(dataFreshnessHeader) != "stale" {
			t.Fatalf("fallback %q overrode stale: %q", fallback, recorder.Header().Get(dataFreshnessHeader))
		}
	}
}

func TestOpenF1HandlerReportsFreshThenStaleSuccess(t *testing.T) {
	year := time.Now().Year()
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = fmt.Fprintf(w, `[{"meeting_key":1,"meeting_name":"British Grand Prix","year":%d}]`, year)
	}))
	client := api.NewOpenF1Client(upstream.URL, time.Second)
	t.Cleanup(func() { _ = client.Close() })
	server := NewServer(client, 0, nil)
	cacheKey := fmt.Sprintf("%s/v1/meetings?year=%d", upstream.URL, year)
	requestURL := fmt.Sprintf("/api/v1/meetings?year=%d&source=openf1", year)
	defer func() {
		// Keep the shared application cache clean even if this test fails.
		db, err := sql.Open("sqlite", api.DefaultCacheDBPath()+"?_busy_timeout=5000")
		if err == nil {
			_, _ = db.Exec(`DELETE FROM cache WHERE key = ?`, cacheKey)
			_ = db.Close()
		}
	}()

	fresh := httptest.NewRecorder()
	server.handleMeetings(fresh, httptest.NewRequest(http.MethodGet, requestURL, nil))
	if fresh.Code != http.StatusOK || fresh.Header().Get(dataSourceHeader) != "openf1" || fresh.Header().Get(dataFreshnessHeader) != "fresh" {
		t.Fatalf("fresh response status/metadata = %d %q/%q body=%s", fresh.Code, fresh.Header().Get(dataSourceHeader), fresh.Header().Get(dataFreshnessHeader), fresh.Body.String())
	}

	db, err := sql.Open("sqlite", api.DefaultCacheDBPath()+"?_busy_timeout=5000")
	if err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(`UPDATE cache SET created_at = ? WHERE key = ?`, time.Now().Add(-48*time.Hour).Unix(), cacheKey); err != nil {
		_ = db.Close()
		t.Fatal(err)
	}
	_ = db.Close()
	upstream.Close()

	stale := httptest.NewRecorder()
	server.handleMeetings(stale, httptest.NewRequest(http.MethodGet, requestURL, nil))
	if stale.Code != http.StatusOK || stale.Header().Get(dataSourceHeader) != "openf1" || stale.Header().Get(dataFreshnessHeader) != "stale" {
		t.Fatalf("stale response status/metadata = %d %q/%q body=%s", stale.Code, stale.Header().Get(dataSourceHeader), stale.Header().Get(dataFreshnessHeader), stale.Body.String())
	}
}

func TestFreshnessHeadersAreExposedToBrowserClients(t *testing.T) {
	handler := withCORS(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		markDataResponse(w, "local", "partial")
		writeJSON(w, map[string]bool{"ok": true})
	}))
	recorder := httptest.NewRecorder()
	handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/api/v1/test", nil))

	if got := recorder.Header().Get("Access-Control-Expose-Headers"); got != dataSourceHeader+", "+dataFreshnessHeader {
		t.Fatalf("exposed headers = %q", got)
	}
	if recorder.Header().Get(dataSourceHeader) != "local" || recorder.Header().Get(dataFreshnessHeader) != "partial" {
		t.Fatalf("data metadata = %q/%q", recorder.Header().Get(dataSourceHeader), recorder.Header().Get(dataFreshnessHeader))
	}
}
