package handlers

import (
	"Wine-Server/utils"
	"Wine-Server/utils/tables"
	"fmt"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"os"
)

type routeFn func(*gin.Engine)
type createTableFn func() error

type App struct {
	router *gin.Engine
	config *utils.Config
}

func lostHandler(ctx *gin.Context) {
	ctx.JSON(utils.HttpNotFound, utils.Fail("api/resource not found"))
}

func loggerFormat(timeFmt string) gin.HandlerFunc {
	return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
		return fmt.Sprintf("[wine] %s - %s [%s %s %s] %s, %s %d %s: %s\n",
			param.ClientIP,
			param.TimeStamp.Format(timeFmt),
			param.MethodColor(), param.Method, param.ResetColor(),
			param.Path,
			param.StatusCodeColor(), param.StatusCode, param.ResetColor(),
			param.ErrorMessage,
		)
	})
}

func CreateApp(config *utils.Config) *App {
	if config.WxMerchantAcc == "" {
		fmt.Println("wx-pay-api config is null")
		os.Exit(-1)
	}
	utils.WxMerchantAcc, utils.WxApiV3Key = config.WxMerchantAcc, config.WxApiV3Key
	utils.WxApiCertSeq, utils.WxApiCertPath = config.WxApiCertSeq, config.WxApiCertPath

	if config.Release {
		gin.SetMode(gin.ReleaseMode)
	} else {
		gin.SetMode(gin.DebugMode)
	}

	router := gin.New()
	err := router.SetTrustedProxies([]string{"127.0.0.1"})
	if err != nil {
		fmt.Printf("can't trust '127.0.0.1', for: %s", err)
		os.Exit(-1)
	}
	if err = utils.GenRsaKey(); err != nil {
		fmt.Println("can't generate private and public key.")
		os.Exit(-1)
	}

	conf := cors.DefaultConfig()
	allow := append(conf.AllowHeaders, "Token", "Device")
	conf.AllowAllOrigins, conf.AllowHeaders = true, allow
	router.Use(loggerFormat(config.TimeFormat), gin.Recovery(), utils.ErrorHandler, cors.New(conf))
	router.NoRoute(lostHandler)
	router.Static("/static", "./static")

	utils.SqlInit(config)
	toCreate := []createTableFn{
		tables.CreateAdvertiseTable, tables.CreateDeviceTable, tables.CreateManagerTable,
		tables.CreateParamsTable, tables.CreateVersionTable, tables.CreateWineTable,
	}
	for _, Fn := range toCreate {
		err = Fn()
		if err != nil {
			fmt.Println("create mysql tables failed.")
			os.Exit(-1)
		}
	}

	return &App{router: router, config: config}
}

func (app *App) RouteRegister(routeFns ...routeFn) {
	for _, fn := range routeFns {
		fn(app.router)
	}
}

func (app *App) Start() {
	err := app.router.Run(app.config.ServerAddr)
	if err != nil {
		fmt.Printf("server can't run on address[%s] for: %s", app.config.ServerAddr, err)
	}
}
