package testing

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
)

// Enumeration values for supported compress Algorithms.
const (
	GZIP = "gzip"
)

type compareCompressFunc func([]byte, io.Reader) error

var allowedAlgorithms = map[string]compareCompressFunc{
	GZIP: GzipCompareCompressBytes,
}

// CompareReaderEmpty checks if the reader is nil, or contains no bytes.
// Returns an error if not empty.
func CompareReaderEmpty(r io.Reader) error {
	if r == nil {
		return nil
	}
	b, err := ioutil.ReadAll(r)
	if err != nil && err != io.EOF {
		return fmt.Errorf("unable to read from reader, %v", err)
	}
	if len(b) != 0 {
		return fmt.Errorf("reader not empty, got\n%v", hex.Dump(b))
	}
	return nil
}

// CompareReaderBytes compares the reader with the expected bytes. Returns an
// error if the two bytes are not equal.
func CompareReaderBytes(r io.Reader, expect []byte) error {
	if r == nil {
		return fmt.Errorf("missing body")
	}
	actual, err := ioutil.ReadAll(r)
	if err != nil {
		return fmt.Errorf("unable to read, %v", err)
	}

	if !bytes.Equal(expect, actual) {
		return fmt.Errorf("bytes not equal\nexpect:\n%v\nactual:\n%v",
			hex.Dump(expect), hex.Dump(actual))
	}
	return nil
}

// CompareJSONReaderBytes compares the reader containing serialized JSON
// document. Deserializes the JSON documents to determine if they are equal.
// Return an error if the two JSON documents are not equal.
func CompareJSONReaderBytes(r io.Reader, expect []byte) error {
	if r == nil {
		return fmt.Errorf("missing body")
	}
	actual, err := ioutil.ReadAll(r)
	if err != nil {
		return fmt.Errorf("unable to read, %v", err)
	}

	if err := JSONEqual(expect, actual); err != nil {
		return fmt.Errorf("JSON documents not equal, %v", err)
	}
	return nil
}

// CompareXMLReaderBytes compares the reader with expected xml byte
func CompareXMLReaderBytes(r io.Reader, expect []byte) error {
	if r == nil {
		return fmt.Errorf("missing body")
	}

	actual, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	if err := XMLEqual(expect, actual); err != nil {
		return fmt.Errorf("XML documents not equal, %w", err)
	}
	return nil
}

// CompareURLFormReaderBytes compares the reader containing serialized URLForm
// document. Deserializes the URLForm documents to determine if they are equal.
// Return an error if the two URLForm documents are not equal.
func CompareURLFormReaderBytes(r io.Reader, expect []byte) error {
	if r == nil {
		return fmt.Errorf("missing body")
	}
	actual, err := ioutil.ReadAll(r)
	if err != nil {
		return fmt.Errorf("unable to read, %v", err)
	}

	if err := URLFormEqual(expect, actual); err != nil {
		return fmt.Errorf("URL query forms not equal, %v", err)
	}
	return nil
}

// CompareCompressedBytes compares the request stream before and after possible request compression
func CompareCompressedBytes(expect *bytes.Buffer, actual io.Reader, disable bool, min int64, algorithm string) error {
	expectBytes := expect.Bytes()
	if disable || int64(len(expectBytes)) < min {
		actualBytes, err := io.ReadAll(actual)
		if err != nil {
			return fmt.Errorf("error while reading request: %q", err)
		}
		if e, a := expectBytes, actualBytes; !bytes.Equal(e, a) {
			return fmt.Errorf("expect content to be %s, got %s", e, a)
		}
	} else {
		compareFn := allowedAlgorithms[algorithm]
		if compareFn == nil {
			return fmt.Errorf("compress algorithm %s is not allowed", algorithm)
		}
		if err := compareFn(expectBytes, actual); err != nil {
			return fmt.Errorf("error while comparing unzipped content: %q", err)
		}
	}

	return nil
}
