package main

import (
	storagehandlers "backend/storage_handlers"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/joho/godotenv"
	"github.com/labstack/echo/v4"
)

var (
	imgbasepath      string
	imageOption      bool
	concurrentImages int = 1
	storagepath      string
	Db               *DbHandler
	Miogest          *MiogestHandler
	ImageManager     *ImageRefManager
	Storage          storagehandlers.StorageHandler
	tempStorageLimit int = 5
)

func main() {
	// SETUP WORK DIRECTORY
	pwd, err := os.Getwd()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	// join paths indipedently of the OS
	imgbasepath = filepath.Join(pwd, "images")
	fmt.Println("Image base path: ", imgbasepath)
	storagepath = filepath.Join(pwd, "storage")

	// SETUP ENV VARIABLES
	err = godotenv.Load(".env")
	if err != nil {
		fmt.Println("Error loading .env file")
	}
	if os.Getenv("IMAGEOPTION") != "" {
		if os.Getenv("IMAGEOPTION") == "true" {
			imageOption = true

		} else {
			imageOption = false
		}
	}
	if os.Getenv("CONCURRENT_IMAGES") != "" {
		concurrentImages, err = strconv.Atoi(os.Getenv("CONCURRENT_IMAGES"))
		if err != nil {
			fmt.Println("Error parsing CONCURRENT_IMAGES:", err)
		}
	}

	// SETUP DATABASE HANDLER
	Db = NewDbHandler()
	if Db == nil {
		log.Fatal("Error connecting to db")
	}
	defer Db.Dbpool.Close()

	// SETUP MIOGEST HANDLER
	Miogest = NewMiogestHandler()
	if Miogest == nil {
		log.Fatal("Error connecting to miogest")
	}

	// SETUP IMAGE MANAGER
	ImageManager = NewImageRefManager()
	if ImageManager == nil {
		log.Fatal("Error creating image manager")
	}

	// SETUP STORAGE HANDLER
	storagehandlers.Storagepath = storagepath
	var strategy storagehandlers.StorageStrategy = storagehandlers.FS
	STORAGE_STRATEGY := os.Getenv("STORAGE_STRATEGY")

	if STORAGE_STRATEGY == "s3" || STORAGE_STRATEGY == "minio" {
		MINIO_URL := os.Getenv("MINIO_URL")
		MINIO_ROOT_USER := os.Getenv("MINIO_ROOT_USER")
		MINIO_ROOT_PASSWORD := os.Getenv("MINIO_ROOT_PASSWORD")

		if MINIO_URL == "" || MINIO_ROOT_USER == "" || MINIO_ROOT_PASSWORD == "" {
			fmt.Println("Error: MINIO_ROOT_USER, MINIO_ROOT_PASSWORD and MINIO_URL must be set")
			strategy = storagehandlers.FS
		} else {
			strategy = storagehandlers.S3
		}
	}

	h, err := storagehandlers.NewStorageHandler(strategy)
	if err != nil {
		log.Fatal("Error creating storage handler: " + err.Error())
	}
	Storage = h
	fmt.Printf("Storage Strategy: %s\n", strategy)

	// SETUP ECHO
	e := SetupRoutes()

	// SETUP PORT
	port := "1323"
	if os.Getenv("ASPNETCORE_PORT") != "" {
		port = os.Getenv("ASPNETCORE_PORT")
	}

	fmt.Println("Image Option: ", imageOption)
	fmt.Println("Concurrent Images: ", concurrentImages)
	fmt.Println("Server url: http://localhost:" + port)

	Conversion("test.jpg")

	// START ECHO
	e.Logger.Info(e.Start(":" + port))
}

type Template struct {
	templates *template.Template
}

func (t *Template) Render(w io.Writer, name string, data any, c echo.Context) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

func SetupRoutes() *echo.Echo {
	funcs := map[string]any{
		"contains":  strings.Contains,
		"hasPrefix": strings.HasPrefix,
		"hasSuffix": strings.HasSuffix,
		"getImage": func(txt string) string {
			return txt[7:]
		},
		"isWebp": func(txt string) bool {
			return strings.Contains(txt, "webp")
		},
	}
	t := &Template{
		templates: template.Must(template.New("").Funcs(funcs).ParseGlob("public/views/*.html")),
	}
	e := echo.New()
	e.Renderer = t

	// MISC
	e.Static("/static", "public/static")
	e.GET("/", func(c echo.Context) error {
		return c.Render(http.StatusOK, "main", map[string]any{
			"imageOption":      imageOption,
			"concurrentImages": concurrentImages,
		})
	})
	e.GET("/test", func(c echo.Context) error {
		return c.String(http.StatusOK, "OK")
	})
	e.POST("/testpost", func(c echo.Context) error {
		// Get the form value
		test := c.FormValue("test")
		fmt.Println("test:", test)
		return c.String(http.StatusOK, "OK")
	})
	e.GET("/health", func(c echo.Context) error {
		_, err := Db.Dbpool.Exec(Db.Ctx, "SELECT 1")
		if err != nil {
			panic(err)
		}
		return c.String(http.StatusOK, "success")
	})

	MiogestRoutes(e)
	BkpRoutes(e)
	StorageRoutes(e)
	ImageRoutes(e)

	return e
}

func BkpRoutes(e *echo.Echo) {
	e.GET("/upload-backup", func(c echo.Context) error {
		return c.Render(http.StatusOK, "backup-upload", nil)
	})
	e.POST("/upload-backup", func(c echo.Context) error {
		file, err := c.FormFile("file")
		if err != nil {
			return err
		}
		src, err := file.Open()
		if err != nil {
			return err
		}
		defer src.Close()
		dst, err := os.Create("bkp.xml")
		if err != nil {
			return err
		}
		defer dst.Close()
		if _, err = io.Copy(dst, src); err != nil {
			return err
		}
		return c.HTML(http.StatusOK, fmt.Sprintf("<p>File %s uploaded successfully.</p>", file.Filename))
	})
	e.GET("/bkp", func(c echo.Context) error {
		data := From_Backup()
		return c.JSONPretty(http.StatusOK, data, "  ")
	})
	e.GET("/parsebkp", func(c echo.Context) error {
		var data = ParseUpdateBkp()
		return c.JSONPretty(http.StatusOK, data, "  ")
	})
	e.GET("/setbkp", func(c echo.Context) error {
		var data = ParseUpdateBkp()
		Db.SetFromBkp(data)
		return c.String(http.StatusOK, "Setted")
	})
}

func MiogestRoutes(e *echo.Echo) {
	// MIOGEST DEFAULTS
	e.GET("/cats", func(c echo.Context) error {
		var data = Miogest.GetCaratteristiche()
		return c.JSONPretty(http.StatusOK, data, "  ")
	})
	e.GET("/categ", func(c echo.Context) error {
		var data = Miogest.GetCategorie()
		return c.JSONPretty(http.StatusOK, data, "  ")
	})

	// MIOGEST UPDATE
	e.GET("/miogest", func(c echo.Context) error {
		data := Miogest.GetAnnunci()
		return c.JSONPretty(http.StatusOK, data, "  ")
	})
	e.GET("/miogest-sample", func(c echo.Context) error {
		xml := Miogest.GetAnnunci()
		rand := rand.Intn(len(xml.Annuncio))
		data := xml.Annuncio[rand]
		return c.JSONPretty(http.StatusOK, data, "  ")
	})
	e.GET("/parse", func(c echo.Context) error {
		start := time.Now()
		var data = Miogest.GetAnnunciParsed()
		end := time.Now()
		fmt.Println("time taken:", end.Sub(start).String())
		return c.JSONPretty(http.StatusOK, data, "  ")
	})
	e.GET("/update", func(c echo.Context) error {
		var data = Miogest.GetAnnunciParsed()
		Db.InsertAnnunciInDB(data)
		return c.String(http.StatusOK, "Updated")
	})
}
