// Package s3 implements a fake s3 server for rclone
package s3

import (
	"context"
	"fmt"
	"math/rand"
	"net/http"

	"github.com/Mikubill/gofakes3"
	"github.com/go-chi/chi/v5"
	"github.com/rclone/rclone/fs"
	"github.com/rclone/rclone/fs/hash"
	httplib "github.com/rclone/rclone/lib/http"
	"github.com/rclone/rclone/vfs"
	"github.com/rclone/rclone/vfs/vfsflags"
)

// Options contains options for the http Server
type Options struct {
	//TODO add more options
	pathBucketMode bool
	hashName       string
	hashType       hash.Type
	authPair       []string
	noCleanup      bool
	HTTP           httplib.Config
}

// Server is a s3.FileSystem interface
type Server struct {
	*httplib.Server
	f       fs.Fs
	vfs     *vfs.VFS
	faker   *gofakes3.GoFakeS3
	handler http.Handler
	ctx     context.Context // for global config
}

// Make a new S3 Server to serve the remote
func newServer(ctx context.Context, f fs.Fs, opt *Options) (s *Server, err error) {
	w := &Server{
		f:   f,
		ctx: ctx,
		vfs: vfs.New(f, &vfsflags.Opt),
	}

	if len(opt.authPair) == 0 {
		fs.Logf("serve s3", "No auth provided so allowing anonymous access")
	}

	var newLogger logger
	w.faker = gofakes3.New(
		newBackend(w.vfs, opt),
		gofakes3.WithHostBucket(!opt.pathBucketMode),
		gofakes3.WithLogger(newLogger),
		gofakes3.WithRequestID(rand.Uint64()),
		gofakes3.WithoutVersioning(),
		gofakes3.WithV4Auth(authlistResolver(opt.authPair)),
		gofakes3.WithIntegrityCheck(true), // Check Content-MD5 if supplied
	)

	w.Server, err = httplib.NewServer(ctx,
		httplib.WithConfig(opt.HTTP),
	)
	if err != nil {
		return nil, fmt.Errorf("failed to init server: %w", err)
	}

	w.handler = w.faker.Server()
	return w, nil
}

// Bind register the handler to http.Router
func (w *Server) Bind(router chi.Router) {
	router.Handle("/*", w.handler)
}

func (w *Server) serve() error {
	w.Serve()
	fs.Logf(w.f, "Starting s3 server on %s", w.URLs())
	return nil
}
