package routes

import (
	"canguidev/shelfy/internal/controllers"
	"canguidev/shelfy/internal/middlewares"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

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


func AddRoutes(superRoute *gin.RouterGroup,db *gorm.DB) {
    userRoutes(superRoute,db)
	testAcces(superRoute)
	loginRoutes(superRoute,db)
	pathRoute(superRoute,db)
	folderRoute(superRoute,db)
	downloadRoute(superRoute,db)
}
func loginRoutes(superRoute *gin.RouterGroup,db *gorm.DB) {
    loginRouter := superRoute.Group("/login")
    {
        loginRouter.POST("/", controllers.Login(db))
    }
}

func userRoutes( superRoute *gin.RouterGroup,db*gorm.DB){
	userRoutes := superRoute.Group("/users")
{
	userRoutes.POST("/",middlewares.CheckAuth(db),controllers.CreateUser(db))
	userRoutes.GET("/", middlewares.CheckAuth(db),controllers.ReadAllUser(db))
	userRoutes.GET("/:id", middlewares.CheckAuth(db),controllers.FindUserById(db))
	userRoutes.PUT("/:id", middlewares.CheckAuth(db),controllers.UpdateUser(db))
	userRoutes.DELETE("/:id", middlewares.CheckAuth(db),controllers.DeleteUser(db))
}
}
func testAcces(superRoute *gin.RouterGroup){
	test := superRoute.Group("/test")
	{
		test.GET("/",controllers.TestPing())
	}
}

func pathRoute (superRoute *gin.RouterGroup,db *gorm.DB){
	pathRoutes := superRoute.Group("/path")
	{
		pathRoutes.POST("/save-path", middlewares.CheckAuth(db),controllers.CreateSavePath(db))
		pathRoutes.GET("/save-path", middlewares.CheckAuth(db),controllers.ReadAllSavePath(db))
		pathRoutes.PUT("/save-path/:id", middlewares.CheckAuth(db),controllers.UpdateSavePath(db))
		pathRoutes.DELETE("/save-path/:id", middlewares.CheckAuth(db),controllers.DeleteSavePath(db))
		pathRoutes.POST("/save-path/validate", middlewares.CheckAuth(db),controllers.PathValidationHandler)
	}

}

func folderRoute (superRoute *gin.RouterGroup,db *gorm.DB){

	folderRoutes :=superRoute.Group("/folders")
	{
		folderRoutes.GET("/",middlewares.CheckAuth(db),controllers.StreamHandler(db))
		folderRoutes.GET("/details",middlewares.CheckAuth(db),controllers.DetailHandler())
		folderRoutes.GET("/file", middlewares.CheckAuth(db), func(c *gin.Context) {
			path := c.Query("path")
			filePath := filepath.Join("upload", filepath.Clean("/"+path))
			    c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
				c.Header("Pragma", "no-cache")
				c.Header("Expires", "0")

				// Supprime le support de "If-Modified-Since" pour forcer le 200
				c.Request.Header.Del("If-Modified-Since")

			ext := strings.ToLower(filepath.Ext(filePath))
			if ext == ".mp4" || ext == ".mkv" || ext == ".webm" || ext == ".mp3" {
				streamFile(c, filePath)
			} else {
				c.File(filePath)
			}
		})


	}


}

func downloadRoute (superRoute *gin.RouterGroup,db *gorm.DB){
	downloadRoutes := superRoute.Group("/download")

	{		
			
		downloadRoutes.GET("/jobs", middlewares.CheckAuth(db), controllers.HandleListJobs(db))
		downloadRoutes.GET("/debrid-status",middlewares.CheckAuth(db),controllers.GetDebridStatus(db))
		downloadRoutes.POST("/account", middlewares.CheckAuth(db),controllers.PostDebridLogin(db))
		downloadRoutes.POST("/add", middlewares.CheckAuth(db),controllers.HandleAddJobsMultiple(db))
		downloadRoutes.POST("/jobs/:id/start", middlewares.CheckAuth(db),controllers.HandleStartJob(db))
		downloadRoutes.POST("/jobs/:id/pause", middlewares.CheckAuth(db),controllers.HandlePauseJob())
		downloadRoutes.POST("/jobs/:id/resume", middlewares.CheckAuth(db),controllers.HandleResumeJob(db))
		downloadRoutes.DELETE("/jobs/:id", middlewares.CheckAuth(db),controllers.HandleDeleteJob(db))
	}

}
  
func streamFile(c *gin.Context, filePath string) {
	log.Printf("[STREAM] ➡️ Requête streaming sur %s", filePath)

	f, err := os.Open(filePath)
	if err != nil {
		log.Printf("[STREAM][ERREUR] Impossible d'ouvrir: %v", err)
		c.Status(http.StatusNotFound)
		return
	}
	defer f.Close()

	fileStat, err := f.Stat()
	if err != nil {
		log.Printf("[STREAM][ERREUR] Stat impossible: %v", err)
		c.Status(http.StatusInternalServerError)
		return
	}

	fileSize := fileStat.Size()
	log.Printf("[STREAM] Taille du fichier: %d octets", fileSize)
	c.Header("Accept-Ranges", "bytes")
	c.Header("Content-Type", mimeTypeByExt(filePath))

	rangeHeader := c.GetHeader("Range")
	log.Printf("[STREAM] Range header reçu: %q", rangeHeader)

	if rangeHeader == "" {
		// Pas de Range, tout le fichier
		c.Header("Content-Length", strconv.FormatInt(fileSize, 10))
		log.Printf("[STREAM] 📦 Pas de Range, envoi du fichier complet (200 OK)")
		http.ServeContent(c.Writer, c.Request, fileStat.Name(), fileStat.ModTime(), f)
		return
	}

	// Analyse du Range
	var start, end int64
	start, end = 0, fileSize-1

	if strings.HasPrefix(rangeHeader, "bytes=") {
		rangeParts := strings.Split(strings.TrimPrefix(rangeHeader, "bytes="), "-")
		if len(rangeParts) == 2 {
			if s, err := strconv.ParseInt(rangeParts[0], 10, 64); err == nil {
				start = s
			}
			if rangeParts[1] != "" {
				if e, err := strconv.ParseInt(rangeParts[1], 10, 64); err == nil {
					end = e
				}
			}
		}
	}
	log.Printf("[STREAM] Parsed range: start=%d, end=%d", start, end)

	if start > end || start < 0 || end >= fileSize {
		log.Printf("[STREAM][ERREUR] Range invalide: %d-%d sur %d", start, end, fileSize)
		c.Status(http.StatusRequestedRangeNotSatisfiable)
		return
	}

	c.Status(http.StatusPartialContent)
	c.Header("Content-Range", "bytes "+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10)+"/"+strconv.FormatInt(fileSize, 10))
	c.Header("Content-Length", strconv.FormatInt(end-start+1, 10))

	log.Printf("[STREAM] ▶️ Envoi bytes %d-%d sur %d [%s]", start, end, fileSize, filePath)
	f.Seek(start, io.SeekStart)
	n, err := io.CopyN(c.Writer, f, end-start+1)
	if err != nil {
		log.Printf("[STREAM][ERREUR] pendant l'envoi: %v", err)
	} else {
		log.Printf("[STREAM] ✔️ Fini, envoyé %d octets", n)
	}
}

// Helper : extension → Content-Type (très basique)
func mimeTypeByExt(filePath string) string {
	ext := strings.ToLower(filepath.Ext(filePath))
	switch ext {
	case ".mp4":
		return "video/mp4"
	case ".mkv":
		return "video/x-matroska"
	case ".webm":
		return "video/webm"
	case ".mp3":
		return "audio/mpeg"
	case ".pdf":
		return "application/pdf"
	default:
		return "application/octet-stream"
	}
}