package playerserver

import (
	"context"
	"encoding/json"
	"io/fs"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func newTestServer(np NowPlaying) *Server {
	return New("127.0.0.1:0", func() NowPlaying { return np })
}

func TestHandlePlayerReturnsHTML(t *testing.T) {
	s := newTestServer(NowPlaying{Status: "running", Connectivity: "online"})
	req := httptest.NewRequest(http.MethodGet, "/player", nil)
	w := httptest.NewRecorder()
	s.handlePlayer(w, req)

	if ct := w.Header().Get("Content-Type"); !strings.Contains(ct, "text/html") {
		t.Fatalf("Content-Type = %q, want text/html", ct)
	}
	body := w.Body.String()
	for _, want := range []string{"<!DOCTYPE html>", "/api/now-playing", "/api/sysinfo", "splash-portrait.png", "splash-landscape.png"} {
		if !strings.Contains(body, want) {
			t.Fatalf("HTML missing %q", want)
		}
	}
}

func TestHandleNowPlayingWithURL(t *testing.T) {
	s := newTestServer(NowPlaying{
		URL:          "https://example.com",
		Status:       "running",
		Connectivity: "online",
	})
	req := httptest.NewRequest(http.MethodGet, "/api/now-playing", nil)
	w := httptest.NewRecorder()
	s.handleNowPlaying(w, req)

	var got NowPlaying
	if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
		t.Fatalf("Unmarshal() error = %v", err)
	}
	if got.URL != "https://example.com" {
		t.Fatalf("URL = %q, want https://example.com", got.URL)
	}
	if got.Connectivity != "online" {
		t.Fatalf("Connectivity = %q, want online", got.Connectivity)
	}
}

func TestHandleNowPlayingWithoutURL(t *testing.T) {
	s := newTestServer(NowPlaying{Status: "running", Connectivity: "degraded"})
	req := httptest.NewRequest(http.MethodGet, "/api/now-playing", nil)
	w := httptest.NewRecorder()
	s.handleNowPlaying(w, req)

	var got NowPlaying
	if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
		t.Fatalf("Unmarshal() error = %v", err)
	}
	if got.URL != "" {
		t.Fatalf("URL = %q, want empty", got.URL)
	}
}

func TestHandleSysInfoReturnsItems(t *testing.T) {
	req := httptest.NewRequest(http.MethodGet, "/api/sysinfo", nil)
	w := httptest.NewRecorder()
	handleSysInfo(w, req)

	if ct := w.Header().Get("Content-Type"); !strings.Contains(ct, "application/json") {
		t.Fatalf("Content-Type = %q, want application/json", ct)
	}
	var got SysInfo
	if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
		t.Fatalf("Unmarshal() error = %v", err)
	}
	// At least hostname should always be present.
	if len(got.Items) == 0 {
		t.Fatal("expected at least one sysinfo item")
	}
	labels := make(map[string]bool)
	for _, item := range got.Items {
		labels[item.Label] = true
		if item.Value == "" {
			t.Errorf("item %q has empty value", item.Label)
		}
	}
	if !labels["Hostname"] {
		t.Error("expected Hostname item in sysinfo")
	}
}

func TestAssetsServed(t *testing.T) {
	// Use httptest recorder to test asset handler directly via the embed FS.
	sub, err := fs.Sub(assetsFS, "assets")
	if err != nil {
		t.Fatalf("fs.Sub error = %v", err)
	}
	handler := http.StripPrefix("/assets/", http.FileServer(http.FS(sub)))

	for _, name := range []string{"splash-landscape.png", "splash-portrait.png"} {
		req := httptest.NewRequest(http.MethodGet, "/assets/"+name, nil)
		w := httptest.NewRecorder()
		handler.ServeHTTP(w, req)
		if w.Code != http.StatusOK {
			t.Errorf("GET /assets/%s = %d, want 200", name, w.Code)
		}
		if ct := w.Header().Get("Content-Type"); !strings.Contains(ct, "image/png") {
			t.Errorf("GET /assets/%s Content-Type = %q, want image/png", name, ct)
		}
	}
}

func TestServerRunAndStop(t *testing.T) {
	s := New("127.0.0.1:0", func() NowPlaying {
		return NowPlaying{Status: "running", Connectivity: "online"}
	})

	ctx, cancel := context.WithCancel(context.Background())
	errCh := make(chan error, 1)
	go func() { errCh <- s.Run(ctx) }()

	cancel()
	if err := <-errCh; err != nil {
		t.Fatalf("Run() error = %v", err)
	}
}
