// Package mux provides support to bind domain level routes
// to the application mux.
package mux

import (
	"embed"
	"net/http"

	"github.com/ardanlabs/kronk/cmd/server/app/sdk/authclient"
	"github.com/ardanlabs/kronk/cmd/server/app/sdk/mid"
	"github.com/ardanlabs/kronk/cmd/server/foundation/logger"
	"github.com/ardanlabs/kronk/cmd/server/foundation/web"
	"github.com/ardanlabs/kronk/sdk/pool"
	buckylibs "github.com/ardanlabs/kronk/sdk/tools/bucky/libs"
	buckymodels "github.com/ardanlabs/kronk/sdk/tools/bucky/models"
	"github.com/ardanlabs/kronk/sdk/tools/libs"
	"github.com/ardanlabs/kronk/sdk/tools/models"
)

// StaticSite represents a static site to run.
type StaticSite struct {
	react       bool
	static      embed.FS
	staticDir   string
	staticPath  string
	apiPrefixes []string
}

// Options represent optional parameters.
type Options struct {
	corsOrigin []string
	sites      []StaticSite
}

// WithCORS provides configuration options for CORS.
func WithCORS(origins []string) func(opts *Options) {
	return func(opts *Options) {
		opts.corsOrigin = origins
	}
}

// WithFileServer provides configuration options for file server.
func WithFileServer(react bool, static embed.FS, dir string, path string, apiPrefixes []string) func(opts *Options) {
	return func(opts *Options) {
		opts.sites = append(opts.sites, StaticSite{
			react:       react,
			static:      static,
			staticDir:   dir,
			staticPath:  path,
			apiPrefixes: apiPrefixes,
		})
	}
}

// Config contains all the mandatory systems required by handlers.
type Config struct {
	Build           string
	Log             *logger.Logger
	AuthClient      *authclient.Client
	Pool            *pool.Pool
	Libs            *libs.Libs
	Models          *models.Models
	BuckyLibs       *buckylibs.Libs
	BuckyModels     *buckymodels.Models
	DownloadEnabled bool
}

// RouteAdder defines behavior that sets the routes to bind for an instance
// of the service.
type RouteAdder interface {
	Add(app *web.App, cfg Config)
}

// WebAPI constructs a http.Handler with all application routes bound.
func WebAPI(cfg Config, routeAdder RouteAdder, options ...func(opts *Options)) http.Handler {
	app := web.NewApp(
		cfg.Log.Info,
		mid.Tracer(),
		mid.Logger(cfg.Log),
		mid.Metrics(),
		mid.Errors(cfg.Log),
		mid.Panics(),
	)

	var opts Options
	for _, option := range options {
		option(&opts)
	}

	if len(opts.corsOrigin) > 0 {
		app.EnableCORS(opts.corsOrigin)
	}

	routeAdder.Add(app, cfg)

	for _, site := range opts.sites {
		switch site.react {
		case true:
			app.FileServerReact(site.static, site.staticDir, site.staticPath, site.apiPrefixes)

		default:
			app.FileServer(site.static, site.staticDir, site.staticPath)
		}
	}

	app.NotFoundHandler()

	return app
}
