package server

import (
	xlstranslator "catalogue.midpdsa.com/modules/xlstranslator"
	"catalogue.midpdsa.com/src/controllers"
	"catalogue.midpdsa.com/src/db/catalogue"
	"catalogue.midpdsa.com/src/middleware"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	csvimporter "catalogue.midpdsa.com/modules/csv"
	"github.com/gin-gonic/gin"
)

func SetupPanelRoutes(r *gin.Engine, ctrl *controllers.AuthController, csvCtrl *controllers.CSVImportController, queries *catalogue.Queries) {
	panel := r.Group("/panel")
	{
		panel.POST("/login", ctrl.Login)
	}

	protected := panel.Group("")
	protected.Use(middleware.AuthMiddleware())
	{
		protected.POST("/uploads/delete", func(c *gin.Context) {
			var req struct {
				Filepaths []string `json:"filepaths" binding:"required"`
			}
			if err := c.ShouldBindJSON(&req); err != nil {
				c.JSON(400, gin.H{"error": err.Error()})
				return
			}

			base := uploadDir()
			var deleted int
			for _, fp := range req.Filepaths {
				fp = filepath.Clean(fp)
				if !strings.HasPrefix(fp, base+string(filepath.Separator)) {
					continue
				}
				if err := os.Remove(fp); err == nil {
					deleted++
				}
			}
			c.JSON(200, gin.H{"deleted": deleted})
		})

		protected.POST("/upload/xlsx", func(c *gin.Context) {
			file, err := c.FormFile("file")
			if err != nil {
				c.JSON(400, gin.H{"error": "No file uploaded"})
				return
			}

			ext := strings.ToLower(filepath.Ext(file.Filename))
			if ext != ".xlsx" && ext != ".xml" {
				c.JSON(400, gin.H{"error": "Only .xlsx or .xml files are allowed"})
				return
			}

			brand := c.PostForm("brand")
			productType := c.PostForm("product_type")
			if brand == "" || productType == "" {
				c.JSON(400, gin.H{"error": "brand and product_type are required"})
				return
			}

			dir := filepath.Join(uploadDir(), brand, productType)
			if err := os.MkdirAll(dir, 0755); err != nil {
				c.JSON(500, gin.H{"error": "Failed to create upload directory"})
				return
			}

			dst := filepath.Join(dir, filepath.Base(file.Filename))
			if err := c.SaveUploadedFile(file, dst); err != nil {
				c.JSON(500, gin.H{"error": "Failed to save file"})
				return
			}

			c.JSON(200, gin.H{
				"message":      "File uploaded successfully",
				"filename":     file.Filename,
				"filepath":     dst,
				"size":         file.Size,
				"brand":        brand,
				"product_type": productType,
			})
		})

		protected.GET("/uploads", handleListUploads)

		protected.POST("/convert", func(c *gin.Context) {
			var req struct {
				Filepath string `json:"filepath" binding:"required"`
				Type     string `json:"type" binding:"required"`
			}
			if err := c.ShouldBindJSON(&req); err != nil {
				c.JSON(400, gin.H{"error": err.Error()})
				return
			}

			base := uploadDir()
			req.Filepath = filepath.Clean(req.Filepath)
			if !strings.HasPrefix(req.Filepath, base+string(filepath.Separator)) {
				c.JSON(400, gin.H{"error": "la ruta del archivo debe estar dentro del directorio de subidas"})
				return
			}
			if _, err := os.Stat(req.Filepath); err != nil {
				c.JSON(400, gin.H{"error": fmt.Sprintf("el archivo no existe: %s", req.Filepath)})
				return
			}

			outDir := controllers.CSVDir()
			if err := os.MkdirAll(outDir, 0755); err != nil {
				c.JSON(500, gin.H{"error": "error al crear el directorio de salida"})
				return
			}

			cfg := xlstranslator.Config{
				InputDir:  filepath.Dir(req.Filepath),
				OutputDir: outDir,
			}
			conv := xlstranslator.NewConverter(cfg)

			if err := conv.ConvertFile(req.Type, req.Filepath); err != nil {
				c.JSON(422, gin.H{"error": err.Error()})
				return
			}

			outputs := xlstranslator.ConverterOutputs(req.Type)
			if err := logConversion(ConversionRecord{
				Filepath: req.Filepath,
				Type:     req.Type,
				Outputs:  outputs,
				CSVDir:   outDir,
			}); err != nil {
				fmt.Printf("failed to log conversion: %v\n", err)
			}

			c.JSON(200, gin.H{
				"message":  "Conversion completed",
				"type":     req.Type,
				"filepath": req.Filepath,
				"outputs":  outputs,
				"csv_dir":  outDir,
			})
		})

		protected.GET("/conversions", func(c *gin.Context) {
			records, err := listConversions(c.Query("type"))
			if err != nil {
				c.JSON(500, gin.H{"error": err.Error()})
				return
			}
			c.JSON(200, records)
		})

		protected.GET("/templates", func(c *gin.Context) {
			c.JSON(200, xlstranslator.TemplateSpecs)
		})

		protected.GET("/convert-types", func(c *gin.Context) {
			c.JSON(200, xlstranslator.ConverterTypes())
		})

		protected.GET("/templates/:name/download", func(c *gin.Context) {
			name := c.Param("name")
			data, mime, err := xlstranslator.GenerateTemplate(name)
			if err != nil {
				c.JSON(404, gin.H{"error": err.Error()})
				return
			}

			spec, _ := xlstranslator.GetTemplateSpec(name)
			c.Header("Content-Type", mime)
			c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", spec.Filename))
			c.Data(200, mime, data)
		})

		protected.POST("/import/csv", csvCtrl.ImportAll)
		protected.GET("/import/files", func(c *gin.Context) {
			files, err := listCSVFiles()
			if err != nil {
				c.JSON(500, gin.H{"error": err.Error()})
				return
			}
			c.JSON(200, files)
		})
		protected.POST("/import/files/delete", func(c *gin.Context) {
			var req struct {
				Filenames []string `json:"filenames" binding:"required"`
			}
			if err := c.ShouldBindJSON(&req); err != nil {
				c.JSON(400, gin.H{"error": err.Error()})
				return
			}

			var deleted int
			for _, name := range req.Filenames {
				if name == "" || strings.Contains(name, string(filepath.Separator)) || name == "." || name == ".." {
					continue
				}
				if err := os.Remove(controllers.CSVPath(name)); err == nil {
					deleted++
				}
			}
			c.JSON(200, gin.H{"deleted": deleted})
		})
		protected.POST("/import/csv/:source", func(c *gin.Context) {
			source := c.Param("source")
			filename := c.DefaultQuery("file", "")
			if filename != "" && (strings.Contains(filename, string(filepath.Separator)) || filename == "." || filename == "..") {
				c.JSON(400, gin.H{"error": "invalid filename"})
				return
			}

			importer := csvimporter.NewImporter(queries)

			var result interface{}
			var err error

			switch source {
			case "ag-shock":
				result, err = importer.ImportAGShock(controllers.CSVPath(csvFilenameOrDefault(filename, "AG_shock.csv")))
			case "ag-strut":
				result, err = importer.ImportAGStrut(controllers.CSVPath(csvFilenameOrDefault(filename, "AG_strut.csv")))
			case "ag-leg":
				result, err = importer.ImportAGLeg(controllers.CSVPath(csvFilenameOrDefault(filename, "AG_leg.csv")))
			case "monroe-ld":
				result, err = importer.ImportMonroeLD(controllers.CSVPath(csvFilenameOrDefault(filename, "monroe_ld.csv")))
			case "monroe-hd":
				result, err = importer.ImportMonroeHD(controllers.CSVPath(csvFilenameOrDefault(filename, "monroe_hd.csv")))
			case "trw-sachs":
				result, err = importer.ImportTRWSachs(controllers.CSVPath(csvFilenameOrDefault(filename, "trw_sachs.csv")))
			case "wagner":
				result, err = importer.ImportWagner(controllers.CSVPath(csvFilenameOrDefault(filename, "wagner_brake.csv")))
			case "brembo":
				result, err = importer.ImportBrembo(controllers.CSVPath(csvFilenameOrDefault(filename, "brembo.csv")))
			case "prestone":
				result, err = importer.ImportPrestone(controllers.CSVPath(csvFilenameOrDefault(filename, "prestone.csv")))
			case "cano":
				result, err = importer.ImportCanoDisk(controllers.CSVPath(csvFilenameOrDefault(filename, "cano_disk.csv")))
			case "grc-brakes":
				result, err = importer.ImportGRCBrakes(controllers.CSVPath(csvFilenameOrDefault(filename, "frenos-deseable.csv")))
			case "grc-shocks":
				result, err = importer.ImportGRCShocksHeavy(controllers.CSVPath(csvFilenameOrDefault(filename, "amort-deseable.csv")))
			case "grc-shocks-ligero":
				result, err = importer.ImportGRCShocks(controllers.CSVPath(csvFilenameOrDefault(filename, "grc-ligro-amort-deseable.csv")))
			case "cano-disk":
				result, err = importer.ImportCanoDisk(controllers.CSVPath(csvFilenameOrDefault(filename, "cano_disk.csv")))
			default:
				c.JSON(400, gin.H{"error": "Unknown import source"})
				return
			}

			if err != nil {
				c.JSON(500, gin.H{"error": err.Error()})
				return
			}

			c.JSON(200, gin.H{
				"message": "Import completed",
				"result":  result,
			})
		})
	}
}

func handleListUploads(c *gin.Context) {
	uploadBase := uploadDir()

	if _, err := os.Stat(uploadBase); os.IsNotExist(err) {
		c.JSON(200, []gin.H{})
		return
	}

	files := make([]gin.H, 0)
	err := filepath.WalkDir(uploadBase, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if d.IsDir() {
			return nil
		}

		info, err := d.Info()
		if err != nil {
			return err
		}

		rel, err := filepath.Rel(uploadBase, path)
		if err != nil {
			return err
		}

		brand := ""
		productType := ""
		parts := strings.Split(rel, string(filepath.Separator))
		if len(parts) >= 3 {
			brand = parts[0]
			productType = parts[1]
		}

		converted, convertedAt := conversionStatusFor(path)
		files = append(files, gin.H{
			"filename":     filepath.Base(path),
			"filepath":     path,
			"brand":        brand,
			"product_type": productType,
			"size":         info.Size(),
			"modified":     info.ModTime().Format(time.RFC3339),
			"converted":    converted,
			"converted_at": convertedAt,
		})
		return nil
	})
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}

	sort.Slice(files, func(i, j int) bool {
		return files[i]["modified"].(string) > files[j]["modified"].(string)
	})

	c.JSON(200, files)
}

func uploadDir() string {
	if dir := os.Getenv("UPLOAD_DIR"); dir != "" {
		return filepath.Clean(dir)
	}
	return "/tmp/uploads"
}

func listCSVFiles() ([]string, error) {
	dir := controllers.CSVDir()
	entries, err := os.ReadDir(dir)
	if err != nil {
		return nil, err
	}

	files := make([]string, 0)
	for _, e := range entries {
		if e.IsDir() {
			continue
		}
		if strings.ToLower(filepath.Ext(e.Name())) == ".csv" {
			files = append(files, e.Name())
		}
	}
	sort.Strings(files)
	return files, nil
}

func csvFilenameOrDefault(filename, def string) string {
	if filename != "" {
		return filename
	}
	return def
}
