package controllers

import (
	"canguidev/shelfy/internal/client"
	runner "canguidev/shelfy/internal/job"
	"canguidev/shelfy/internal/models"
	"context"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"

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

func sanitizeFileName(name string) string {
  return runner.SanitizeFileName(name)
}
var seriesRegex = regexp.MustCompile(`^(.+?)\.S\d{2}E\d{2}`)
var (
	jobs   = make(map[string]*runner.DownloadJob)
	jobsMu sync.Mutex
)
func HandleAddJobsMultiple(db *gorm.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 1. Parsing des champs du formulaire
		var form struct {
   		 Links  string `json:"links" form:"links" binding:"required"`
   		 PathID string `json:"path_id" form:"path_id" binding:"required"`
		}

		// On tente d'abord le bind JSON
		if err := c.ShouldBindJSON(&form); err != nil {
			log.Printf("[DEBUG] Reçu links=%q | path_id=%q", form.Links, form.PathID)
			// Si JSON échoue, on tente avec form-urlencoded
			if err := c.ShouldBind(&form); err != nil {
				log.Printf("[DEBUG] Reçu links=%q | path_id=%q", form.Links, form.PathID)
				c.JSON(http.StatusBadRequest, gin.H{"error": "Champs requis manquants ou invalides"})
				return
			}
			}


		lines := strings.Split(form.Links, "\n")
		baseID, err := strconv.ParseInt(form.PathID, 10, 64)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "ID de chemin invalide"})
			return
		}

		// 2. Vérification du dossier principal
			var paths []models.PathDownload
			if err := db.Find(&paths).Error; err != nil {
				log.Printf("[DEBUG] Erreur lors du Find: %v", err)
			} else {
				log.Printf("[DEBUG] Contenu de la table PathDownload avant recherche: %+v", paths)
			}

			var basePath models.PathDownload
			if err := db.First(&basePath, baseID).Error; err != nil {
				log.Printf("[DEBUG] Pas trouvé baseID=%v dans PathDownload", baseID)
				c.JSON(http.StatusBadRequest, gin.H{"error": "Dossier principal introuvable"})
				return
			}
			log.Printf("[DEBUG] PathDownload trouvé pour baseID=%v: %+v", baseID, basePath)


		// 3. Initialisation Debrid-Link
		ctx := context.Background()
		clt := client.NewClient(db)
		account := runner.GetFirstActiveAccount(clt)
		if account == nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte Debrid-Link actif"})
			return
		}
		clt.SetAccount(account)

		// 4. Résultats à retourner
		var results []map[string]interface{}

		// 5. Itération des liens
		for _, link := range lines {
			link = strings.TrimSpace(link)
			if link == "" {
				continue
			}

			links, err := clt.AddLink(ctx, link)
			if err != nil {
				log.Printf("Échec débridage de %s: %v", link, err)
				results = append(results, gin.H{"link": link, "status": "failed", "error": err.Error()})
				continue
			}

			for _, l := range links {
				clean := sanitizeFileName(l.Name)
				series := clean
				if m := seriesRegex.FindStringSubmatch(clean); len(m) == 2 {
					series = m[1]
				}

				assignID := int(basePath.ID)
				if series != "" {
					dirPath := filepath.Join(basePath.Path, series)
					if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
						log.Printf("Erreur création dossier %s: %v", dirPath, err)
					}

					var sub models.PathDownload
					if err := db.Where("path = ?", dirPath).First(&sub).Error; err != nil {
						if err == gorm.ErrRecordNotFound {
							sub = models.PathDownload{Path: dirPath, PathName: series}
							if err := db.Create(&sub).Error; err != nil {
								log.Printf("Erreur création PathDownload: %v", err)
							}
						} else {
							log.Printf("Erreur lecture PathDownload: %v", err)
						}
					}
					assignID = int(sub.ID)
				}

				// streamInfo, err := clt.CreateTranscode(ctx, l.ID)
				// if err != nil {
				// 	log.Printf("Erreur transcode pour %s: %v", l.ID, err)
				// }

				job := &runner.DownloadJob{
					ID:        l.ID,
					Link:      l.DownloadURL,
					Name:      l.Name,
					Status:    "waiting",
					PathID:    assignID,
					Size:      l.Size,
					Host:      l.Host,
					Progress:  0,
					StreamURL: "",
				}
				// if streamInfo != nil {
				// 	job.StreamURL = streamInfo.StreamURL
				// }

				if err := runner.RegisterJobWithDB(job, db); err != nil {
					log.Printf("Erreur enregistrement job: %v", err)
					results = append(results, gin.H{"link": l.URL, "status": "failed", "error": err.Error()})
					continue
				}

				results = append(results, gin.H{"link": l.URL, "status": "added", "name": job.Name})
			}
		}

		// 6. Broadcast au frontend
		runner.Broadcast()

		c.JSON(http.StatusOK, gin.H{
			"message": "Traitement terminé",
			"results": results,
		})
	}
}


func HandleStartJob(db *gorm.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		id := c.Param("id")
		log.Printf("[StartJob] ID = %s", id)

		// 1. Vérifie si le job est déjà en mémoire
		jobsMu.Lock()
		job, exists := jobs[id]
		jobsMu.Unlock()

		// 2. Sinon, récupère depuis la BDD
		if !exists {
			var j runner.DownloadJob
			if err := db.First(&j, "id = ?", id).Error; err != nil {
				c.JSON(http.StatusNotFound, gin.H{"error": "Job introuvable"})
				return
			}
			jobCopy := j
			jobsMu.Lock()
			jobs[id] = &jobCopy
			job = &jobCopy
			jobsMu.Unlock()
		}

		clt:= client.NewClient(db)
		account := runner.GetFirstActiveAccount(clt)
		if account == nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte Debrid-Link actif"})
			return
		}
		clt.SetAccount(account)

		go runner.StartDownload(job, job.Link, clt, db)
		runner.Broadcast()

		c.Status(http.StatusNoContent)
	}
}
func HandlePauseJob() gin.HandlerFunc {
	return func(c *gin.Context) {
		id := c.Param("id")
		runner.UpdateJobStatus(id, "paused", nil)
		runner.Broadcast()

		c.Status(http.StatusNoContent)
	}
}
func HandleResumeJob(db *gorm.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		id := c.Param("id")

		jobsMu.Lock()
		job, exists := jobs[id]
		jobsMu.Unlock()

		if !exists {
			var j runner.DownloadJob
			if err := db.First(&j, "id = ?", id).Error; err != nil {
				c.JSON(http.StatusNotFound, gin.H{"error": "Job introuvable"})
				return
			}
			jobCopy := j
			jobsMu.Lock()
			jobs[id] = &jobCopy
			job = &jobCopy
			jobsMu.Unlock()
		}

		clt := client.NewClient(db)
		account := runner.GetFirstActiveAccount(clt)
		if account == nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte actif"})
			return
		}
		clt.SetAccount(account)

		go runner.StartDownload(job, job.Link, clt, db)
		runner.Broadcast()

		c.Status(http.StatusNoContent)
	}
}
func HandleDeleteJob(db *gorm.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		id := c.Param("id")
		runner.DeleteJob(id, db)
		go runner.Broadcast()

		c.Status(http.StatusNoContent)
	}
}
func HandleListJobs(db *gorm.DB) gin.HandlerFunc {
    return func(c *gin.Context) {
        var jobs []runner.DownloadJob
        // Optionnel : filtrage (par user, status, etc.)
        if err := db.Find(&jobs).Error; err != nil {
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }
        c.JSON(200, gin.H{
            "jobs": jobs,
        })
    }
}



