package api

import (
	"fmt"
	"io"
	"machines/app/cfg"
	"machines/app/db"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

func handlePing(c *gin.Context) {
	c.JSON(
		http.StatusOK,
		PingDto{
			Message: "Pong!",
		},
	)
}

func handleGetMachines(dbConn *gorm.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		machines := db.GetMachines(dbConn)

		c.JSON(
			http.StatusOK,
			GetMachinesResponse{
				Machines: *machines,
			},
		)
	}
}

func handleGetMachine(dbConn *gorm.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		machineId, err := strconv.Atoi(c.Param("machineId"))
		if err != nil {
			raiseBadRequestError(c, "Invalid machineId parameter")
			return
		}

		machine, err := db.GetMachine(dbConn, machineId)
		if err != nil {
			raiseNotFoundError(c, "Machine not found")
			return
		}

		c.JSON(
			http.StatusOK,
			machine,
		)
	}
}

func handleGetMachineProducts(dbConn *gorm.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		machineId, err := strconv.Atoi(c.Param("machineId"))
		if err != nil {
			raiseBadRequestError(c, "Invalid machineId parameter")
			return
		}

		machine, err := db.GetMachine(dbConn, machineId)
		if err != nil {
			raiseNotFoundError(c, "Machine not found")
			return
		}

		url := fmt.Sprintf("%s/products?machineId=%d", cfg.Config.ProductsAppUrl, machine.Id)
		resp, err := http.Get(url)
		if err != nil {
			fmt.Println(err.Error())
			raiseInternalError(c, err.Error())
			return
		}

		if resp.Body != nil {
			defer resp.Body.Close()
		}

		body, err := io.ReadAll(resp.Body)
		if err != nil {
			fmt.Println(err.Error())
			raiseInternalError(c, err.Error())
			return
		}

		c.Header("Content-Type", "application/json; charset=utf-8")
		c.Writer.WriteString(string(body))
	}
}

func initRouter(dbConn *gorm.DB) *gin.Engine {
	gin.SetMode(gin.ReleaseMode)
	router := gin.Default()
	router.Use(corsMiddleware())

	routes := router.Group("/machines")
	{
		routes.GET("/ping", handlePing)
		routes.GET("", handleGetMachines(dbConn))
		routes.GET("/:machineId", handleGetMachine(dbConn))
		routes.GET("/:machineId/products", handleGetMachineProducts(dbConn))
	}

	return router
}

func Serve(dbConn *gorm.DB) {
	serverAddr := fmt.Sprintf("%s:%d", cfg.Config.AppHost, cfg.Config.AppPort)
	fmt.Printf("Starting serving on %s\n", serverAddr)

	router := initRouter(dbConn)
	router.Run(serverAddr)
}
