package renderer

import (
	"bytes"
	"encoding/hex"
	"strings"
	"testing"
	"unicode/utf16"

	"github.com/tech/sendico/billing/documents/internal/content"
)

func TestRenderer_RenderContainsText(t *testing.T) {
	blocks := []Block{
		{Tag: TagTitle, Lines: []string{"ACT"}},
		{Tag: TagText, Lines: []string{"Executor: Jane Doe", "Amount: 100 USD"}},
	}

	r := Renderer{
		Issuer: Issuer{
			LegalName:    "Sendico Ltd",
			LegalAddress: "12 Market Street, London, UK",
		},
		OwnerPassword: "",
	}

	pdfBytes, err := r.Render(blocks, "deadbeef")
	if err != nil {
		t.Fatalf("Render: %v", err)
	}

	if len(pdfBytes) == 0 {
		t.Fatalf("expected PDF bytes")
	}

	checks := []string{"Sendico Ltd", "Jane Doe", "100 USD", strings.TrimSpace(strings.TrimSuffix(content.DocumentIntegrityHashPrefix, ": "))}

	for _, token := range checks {
		if !containsPDFText(pdfBytes, token) {
			t.Fatalf("expected PDF to contain %q", token)
		}
	}
}

func containsPDFText(pdfBytes []byte, text string) bool {
	if bytes.Contains(pdfBytes, []byte(text)) {
		return true
	}

	hexText := hex.EncodeToString([]byte(text))

	if bytes.Contains(pdfBytes, []byte(strings.ToUpper(hexText))) {
		return true
	}

	if bytes.Contains(pdfBytes, []byte(strings.ToLower(hexText))) {
		return true
	}

	utf16Bytes := encodeUTF16BE(text, false)

	if bytes.Contains(pdfBytes, utf16Bytes) {
		return true
	}

	utf16Hex := hex.EncodeToString(utf16Bytes)

	if bytes.Contains(pdfBytes, []byte(strings.ToUpper(utf16Hex))) {
		return true
	}

	if bytes.Contains(pdfBytes, []byte(strings.ToLower(utf16Hex))) {
		return true
	}

	utf16BytesBOM := encodeUTF16BE(text, true)
	if bytes.Contains(pdfBytes, utf16BytesBOM) {
		return true
	}

	utf16HexBOM := hex.EncodeToString(utf16BytesBOM)

	if bytes.Contains(pdfBytes, []byte(strings.ToUpper(utf16HexBOM))) {
		return true
	}

	return bytes.Contains(pdfBytes, []byte(strings.ToLower(utf16HexBOM)))
}

func encodeUTF16BE(text string, withBOM bool) []byte {
	encoded := utf16.Encode([]rune(text))
	length := len(encoded) * 2

	if withBOM {
		length += 2
	}

	out := make([]byte, 0, length)

	if withBOM {
		out = append(out, 0xFE, 0xFF)
	}

	for _, v := range encoded {
		out = append(out, byte(v>>8), byte(v&0x00FF))
	}

	return out
}
