package api

import (
	"archive/zip"
	"bytes"
	"io"
	"net/http/httptest"
	"testing"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/compress"
)

// buildTinyXLSX returns a minimal but structurally valid .xlsx (ZIP) payload.
func buildTinyXLSX(t *testing.T) []byte {
	t.Helper()
	var buf bytes.Buffer
	zw := zip.NewWriter(&buf)
	entries := map[string]string{
		"[Content_Types].xml": `<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/></Types>`,
		"_rels/.rels":         `<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`,
		"xl/workbook.xml":     `<?xml version="1.0"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheets><sheet name="Sheet1" sheetId="1" r:id="rId1"/></sheets></workbook>`,
	}
	for name, content := range entries {
		w, err := zw.Create(name)
		if err != nil {
			t.Fatalf("zip create %s: %v", name, err)
		}
		if _, err := io.WriteString(w, content); err != nil {
			t.Fatalf("zip write %s: %v", name, err)
		}
	}
	if err := zw.Close(); err != nil {
		t.Fatalf("zip close: %v", err)
	}
	return buf.Bytes()
}

func assertValidZip(t *testing.T, b []byte) {
	t.Helper()
	if _, err := zip.NewReader(bytes.NewReader(b), int64(len(b))); err != nil {
		t.Fatalf("payload is not a valid zip: %v", err)
	}
}

func TestIsWOPIRequest(t *testing.T) {
	app := fiber.New()
	cases := []struct {
		path string
		want bool
	}{
		{"/wopi/files/123/contents", true},
		{"/wopi/files/123", true},
		{"/api/file-manager/files", false},
		{"/wopimirror/x", false},
		{"/", false},
	}
	for _, tc := range cases {
		app.Get(tc.path, func(c *fiber.Ctx) error {
			if got := isWOPIRequest(c); got != tc.want {
				t.Errorf("isWOPIRequest(%q) = %v, want %v", tc.path, got, tc.want)
			}
			return c.SendStatus(fiber.StatusOK)
		})
		resp, err := app.Test(httptest.NewRequest("GET", tc.path, nil), -1)
		if err != nil {
			t.Fatalf("app.Test(%q): %v", tc.path, err)
		}
		_ = resp.Body.Close()
	}
}

// Regression for the "template .xlsx always corrupt via WOPI" bug: the global
// gzip compress middleware must NOT compress WOPI file-content responses.
// Collabora saves the response body verbatim as the document, so a gzip stream
// there is a guaranteed-corrupt file.
func TestCompressMiddleware_SkipsWOPIContents(t *testing.T) {
	src := buildTinyXLSX(t)
	assertValidZip(t, src)

	app := fiber.New()
	// Mirror the production compress wiring (UseDefaultMiddlewares).
	app.Use(compress.New(compress.Config{
		Level: compress.LevelBestSpeed,
		Next: func(c *fiber.Ctx) bool {
			return isEventStreamRequest(c) || isWOPIRequest(c)
		},
	}))

	streamXLSX := func(c *fiber.Ctx) error {
		c.Set(fiber.HeaderContentType, "application/octet-stream")
		c.Status(fiber.StatusOK)
		c.Context().Response.SetBodyStream(io.NopCloser(bytes.NewReader(src)), len(src))
		return nil
	}
	app.Get("/wopi/files/:id/contents", streamXLSX)
	// Negative control: a non-WOPI route with a compressible body must still gzip.
	app.Get("/api/echo", func(c *fiber.Ctx) error {
		c.Set(fiber.HeaderContentType, "application/octet-stream")
		return c.Send(bytes.Repeat([]byte("A"), 4096))
	})

	// WOPI content download with gzip offered must come back raw + valid.
	req := httptest.NewRequest("GET", "/wopi/files/abc/contents", nil)
	req.Header.Set("Accept-Encoding", "gzip")
	resp, err := app.Test(req, -1)
	if err != nil {
		t.Fatalf("app.Test wopi: %v", err)
	}
	if enc := resp.Header.Get("Content-Encoding"); enc != "" {
		t.Fatalf("WOPI content unexpectedly encoded: Content-Encoding=%q", enc)
	}
	got, _ := io.ReadAll(resp.Body)
	_ = resp.Body.Close()
	assertValidZip(t, got)
	if !bytes.Equal(got, src) {
		t.Fatalf("WOPI content body mutated: got %d bytes, want %d", len(got), len(src))
	}

	// Sanity: prove compression is actually active for non-excluded routes.
	req2 := httptest.NewRequest("GET", "/api/echo", nil)
	req2.Header.Set("Accept-Encoding", "gzip")
	resp2, err := app.Test(req2, -1)
	if err != nil {
		t.Fatalf("app.Test echo: %v", err)
	}
	if enc := resp2.Header.Get("Content-Encoding"); enc != "gzip" {
		t.Fatalf("expected gzip on non-WOPI route, got Content-Encoding=%q (compress not active - test invalid)", enc)
	}
	_ = resp2.Body.Close()
}
