// Package res defines all resources
package res

import (
	"embed"
	"errors"
	"io"
	"io/fs"
	"log/slog"
	"net/http"

	"quantex.com/qfixdpl/src/common/tracerr"
)

var resources fs.FS //nolint

func Set(r fs.FS) {
	resources = r
}

func Get() fs.FS {
	return resources
}

// ReadFile reads the named file and returns the contents.
// Got it from Go Standard Library: /usr/local/go/src/os/file.go
// A successful call returns err == nil, not err == EOF.
// Because ReadFile reads the whole file, it does not treat an EOF from Read
// as an error to be reported.
func ReadFile(name string) ([]byte, error) {
	switch assets := resources.(type) {
	case embed.FS:
		b, err := assets.ReadFile(name)
		if err != nil {
			return nil, tracerr.Errorf("error reading file for embed.FS: %w", err)
		}

		return b, nil
	case fs.FS:
		b, err := readFileInternal(name)
		if err != nil {
			return nil, tracerr.Errorf("error reading file for embed.FS: %w", err)
		}

		return b, nil
	default:
		return nil, tracerr.Errorf("not allowed FS type: %T", assets)
	}
}

//revive:disable:cognitive-complexity we need this level of complexity
//revive:disable:cyclomatic we need this level of complexity
func readFileInternal(name string) ([]byte, error) {
	file, err := resources.Open(name)
	if err != nil {
		return nil, tracerr.Errorf("error opening file %s: %w", name, err)
	}

	defer func() {
		e := file.Close()
		if err != nil || e != nil {
			err = tracerr.Errorf("error closing file %s: %w", e, err)
		}
	}()

	var size int

	if info, err := file.Stat(); err == nil {
		size64 := info.Size()
		if int64(int(size64)) == size64 {
			size = int(size64)
		}
	}

	size++ // one byte for final read at EOF

	// If a file claims a small size, read at least 512 bytes.
	// In particular, files in Linux's /proc claim size 0 but
	// then do not work right if read in small pieces,
	// so an initial read of 1 byte would not work correctly.
	if size < 512 {
		size = 512
	}

	data := make([]byte, 0, size)

	for {
		if len(data) >= cap(data) {
			d := append(data[:cap(data)], 0)
			data = d[:len(data)]
		}

		n, err := file.Read(data[len(data):cap(data)])
		data = data[:len(data)+n]

		if err != nil {
			if errors.Is(err, io.EOF) {
				err = nil
			}

			return data, err
		}
	}
}

//revive:enable:cyclomatic
//revive:enable:cognitive-complexity

func GetFileSystem(prefix string) http.FileSystem {
	fsys, err := fs.Sub(resources, prefix)
	if err != nil {
		e := tracerr.Errorf("error with prefix %s: %w", prefix, err)

		slog.Error(e.Error())
	}

	return http.FS(fsys)
}
