package web

import (
	"fmt"
	"github.com/eikona-org/eikona/v2/controller"
	"github.com/eikona-org/eikona/v2/data/repositories"
	_ "github.com/eikona-org/eikona/v2/docs" // docs is generated by Swag CLI, you have to import it.
	"github.com/eikona-org/eikona/v2/middleware"
	"github.com/eikona-org/eikona/v2/service"
	"github.com/eikona-org/eikona/v2/storage"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
)

var (
	imgRepo           = repositories.ImageRepository{}
	orgRepo           = repositories.OrganizationRepository{}
	procRepo          = repositories.ProcessRepository{}
	procStepRepo      = repositories.ProcessingStepRepository{}
	userRepo          = repositories.UserRepository{}
	storageClient     = storage.NewClient()
	authService       = service.NewAuthService(userRepo, orgRepo, storageClient)
	cacheService      = service.NewCacheService()
	imageService      = service.NewImageService(imgRepo, userRepo, storageClient)
	jwtService        = service.NewJWTService()
	renderService     = service.NewRenderService(imgRepo, procRepo, storageClient)
	processService    = service.NewProcessService(procRepo, procStepRepo, userRepo)
	authController    = controller.NewAuthController(authService, jwtService)
	imageController   = controller.NewImageController(imageService, jwtService)
	processController = controller.NewProcessController(processService, jwtService)
	renderController  = controller.NewRenderController(renderService, cacheService)
)

func Serve() {
	server := gin.Default()
	// Used for simpler developing - can be removed later or adjusted only for public api route
	server.Use(CORS(), RequestCancelRecover())

	publicEndpoints := server.Group("/api")
	{
		// API Doc -> GET /api/doc - https://github.com/swaggo/gin-swagger
		publicEndpoints.GET("/doc/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
		// Login -> POST /api/login
		publicEndpoints.POST("/login", authController.Login)
		// Register -> POST /api/register
		publicEndpoints.POST("/register", authController.Register)
		// Dynamic render -> GET /api/render/dynamic/<img-id>?<params>
		publicEndpoints.GET("/render/dynamic/:identifier", renderController.DynamicRender)
		// Pipeline render -> GET /api/render/pipeline/<img-id>/<proc-id>
		publicEndpoints.GET("/render/pipeline/:identifier/:process", renderController.PipelineRender)
	}

	protectedEndpoints := publicEndpoints.Group("/auth", middleware.AuthorizeJWT(jwtService))
	{
		// -> GET /api/auth/images
		protectedEndpoints.GET("/images", imageController.ListAllImages)
		// -> GET /api/auth/processes
		protectedEndpoints.GET("/processes", processController.ListAllProcesses)
		// -> GET /api/auth/processingsteptypes
		protectedEndpoints.GET("/processingsteptypes", processController.ListAllProcessingStepTypes)
		// -> POST /api/auth/process
		protectedEndpoints.POST("/process", processController.CreateProcess)
		// -> POST /api/auth/processingstep
		protectedEndpoints.POST("/processingstep", processController.AttachStepToProcess)
		// -> POST /api/auth/processingsteps
		protectedEndpoints.POST("/processingsteps", processController.AttachStepsToProcess)
		// -> POST /api/auth/upload
		protectedEndpoints.POST("/upload", imageController.UploadImage)
	}

	server.Run(":8080") //TODO: Make this configurable
}

// Used for simpler developing - can be removed later or adjusted only for public api route
// https://github.com/gin-contrib/cors/issues/29
func CORS() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}

		c.Next()
	}
}

func RequestCancelRecover() gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println("A problem occured")
				c.Request.Context().Done()
			}
		}()
		c.Next()
	}
}
