// Package rest defines all API rest functionality
package rest

import (
	"log/slog"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gomodule/redigo/redis"

	"quantex.com/qfixpt/src/app"
	"quantex.com/qfixpt/src/app/version"
	"quantex.com/qfixpt/src/client/store"
	"quantex.com/qfixpt/src/common/logger"
	"quantex.com/qfixpt/src/common/tracerr"
	"quantex.com/qfixpt/src/domain"
)

const RedisMaxIdle = 3000 // In ms

type API struct {
	Router     *gin.Engine
	Controller *Controller
	Port       string
}

type Config struct {
	AllowedOrigins     []string
	External           map[string]app.ExtAuth           `toml:"External"`
	AuthorizedServices map[string]app.AuthorizedService `toml:"AuthorizedServices"`
	Port               string
	EnableJWTAuth      bool
}

// TradeProvider exposes trade data for the REST API.
type TradeProvider interface {
	TradeCaptureReports() []domain.TradeCaptureReport
	GetAllTrades() []domain.TradeCaptureReport
}

func New(userData app.UserDataProvider, storeInstance *store.Store, tradeProvider TradeProvider, config Config, notify domain.Notifier) *API {
	// Set up Gin
	var engine *gin.Engine
	if version.Environment() == version.EnvironmentTypeProd {
		gin.SetMode(gin.ReleaseMode)
		engine = gin.New()
		engine.Use(gin.Recovery())
		// Use a custom logger middleware
		engine.Use(logger.GinLoggerMiddleware(slog.Default()))
	} else {
		gin.SetMode(gin.DebugMode)
		engine = gin.New()
		// Don't use recovery middleware in debug mode
		engine.Use(gin.Logger())
	}

	err := engine.SetTrustedProxies([]string{"127.0.0.1"})
	if err != nil {
		panic("error setting trusted proxies: %v" + err.Error())
	}

	if config.Port == "" {
		panic("API Base Port can not be empty!")
	}

	api := &API{
		Controller: newController(NewPool(), userData, storeInstance, tradeProvider, config, notify),
		Router:     engine,
		Port:       config.Port,
	}

	SetRoutes(api)

	return api
}

func NewPool() *redis.Pool {
	return &redis.Pool{
		MaxIdle:     RedisMaxIdle,
		IdleTimeout: 1 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.DialURL("redis://localhost")
			if err != nil {
				return nil, tracerr.Errorf("error connecting to Redis: %w", err)
			}

			return c, nil
		},
	}
}

// Run starts the API
func (api *API) Run() {
	// Gin blocks the calling gorutine, so we start it in its own gorutine
	// calling directly go api.Router.Run doesn't prevent having to press double
	// ctrl+c to stop the service
	go func() {
		// start the server
		slog.Error(api.Router.Run("localhost:" + api.Port).Error())
	}()
}
