package main

import (
	"backend/config"
	"backend/queries"
	"backend/typesdefs"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"
	"text/template"
	"time"

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

type Server struct {
	miogest *MiogestHandler
	config  *config.Config
}

func NewServer(cfg *config.Config) (*Server, error) {
	miogest := NewMiogestHandler()
	if miogest == nil {
		return nil, fmt.Errorf("error connecting to miogest")
	}
	return &Server{
		miogest: miogest,
		config:  cfg,
	}, nil
}

func (s *Server) 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":      s.config.Images.Enabled,
			"concurrentImages": s.config.Images.ConcurrentLimit,
		})
	})
	e.GET("/test", func(c echo.Context) error {
		files, err := ListBucket("images")
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}

		return c.JSONPretty(http.StatusOK,
			map[string]any{
				"files": files,
			}, "  ")
	})
	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 {
		h := queries.HealthCheck()

		s_error := HealthCheckStorage()
		var s string
		if s_error != nil {
			s = s_error.Error()
		} else {
			s = "OK"
		}
		return c.JSONPretty(http.StatusOK, fmt.Sprintf("DB Connection: %v, Storage Connection: %v", h, s), "  ")
	})

	// MIOGEST ROUTES
	e.GET("/cats", func(c echo.Context) error {
		var data = s.miogest.GetCaratteristiche()
		return c.JSONPretty(http.StatusOK, data, "  ")
	})
	e.GET("/categ", func(c echo.Context) error {
		var data = s.miogest.GetCategorie()
		return c.JSONPretty(http.StatusOK, data, "  ")
	})

	e.GET("/miogest", func(c echo.Context) error {
		data, err := s.miogest.GetAnnunci()
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		return c.JSONPretty(http.StatusOK, data, "  ")
	})
	e.GET("/miogest-sample", func(c echo.Context) error {
		xml, err := s.miogest.GetAnnunci()
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		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, err = s.miogest.GetAnnunciParsed(nil, nil, false)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		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 {
		forceMediaStale := false
		forceMediaStr := c.QueryParam("forcemedia")
		if forceMediaStr == "true" {
			forceMediaStale = true
		}
		imgPool := []typesdefs.MediaToProcess{}
		videoPool := []typesdefs.MediaToProcess{}

		data, err := s.miogest.GetAnnunciParsed(&imgPool, &videoPool, forceMediaStale)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		err = queries.AnnunciInsert(data, true)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}

		imgRefs, err := ProcessImagePool(&imgPool)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		err = queries.SetImagesToDB(&imgRefs)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}

		videoRefs, err := ProcessVideoPool(&videoPool)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		err = queries.SetVideosToDB(&videoRefs)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		/*
			err = invalidateAllAnnunciFB()
			if err != nil {
				return c.String(http.StatusInternalServerError, err.Error())
			}
		*/
		log.Println("Update completed")
		return c.String(http.StatusOK, "Updated")
	})
	e.GET("/update-cod/:cod", func(c echo.Context) error {

		cod := c.Param("cod")
		imgPool := []typesdefs.MediaToProcess{}
		videoPool := []typesdefs.MediaToProcess{}
		data, err := s.miogest.GetAnnuncioParsed(cod, &imgPool, &videoPool)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		err = queries.AnnunciInsert(data, false)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}

		imgRefs, err := ProcessImagePool(&imgPool)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		err = queries.SetImagesToDB(&imgRefs)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}

		videoRefs, err := ProcessVideoPool(&videoPool)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		err = queries.SetVideosToDB(&videoRefs)
		if err != nil {
			return c.String(http.StatusInternalServerError, err.Error())
		}
		/*
			url := fmt.Sprintf("https://www.infoalloggi.it/annuncio/%s", cod)
			err = invalidateFacebookCache(url)
			if err != nil {
				return c.String(http.StatusInternalServerError, err.Error())
			}
		*/
		log.Printf("Update %s completed", cod)
		return c.String(http.StatusOK, "Updated")
	})

	//IMAGE ROUTES
	e.GET("/initcwebp", func(c echo.Context) error {
		Conversion("test.jpg")
		return c.String(http.StatusOK, "Cwebp initialized")
	})

	e.POST("/image-option-toggle", func(c echo.Context) error {
		// Toggle the imageOption value
		s.config.Images.Enabled = !s.config.Images.Enabled

		// Convert the boolean value to a string and set the environment variable
		os.Setenv("IMAGEOPTION", strconv.FormatBool(s.config.Images.Enabled))

		return c.String(http.StatusOK, "OK")
	})

	return e
}

func (s *Server) Start() {
	e := s.SetupRoutes()

	Conversion("test.jpg")
	fmt.Println("Image Option: ", s.config.Images.Enabled)
	fmt.Println("Concurrent Images: ", s.config.Images.ConcurrentLimit)
	fmt.Println("Server url: http://localhost:" + strconv.Itoa(s.config.Server.Port))

	e.Logger.Info(e.Start(":" + strconv.Itoa(s.config.Server.Port)))
}

func main() {
	cfg := config.Load()
	server, err := NewServer(cfg)
	if err != nil {
		log.Fatalf("Server initialization error: %v", err)
	}
	server.Start()
}

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)
}
