package handlers

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"git.mchus.pro/mchus/quoteforge/internal/middleware"
	"git.mchus.pro/mchus/quoteforge/internal/models"
	"git.mchus.pro/mchus/quoteforge/internal/services"
	"github.com/gin-gonic/gin"
)

type ExportHandler struct {
	exportService  *services.ExportService
	configService  services.ConfigurationGetter
	projectService *services.ProjectService
}

func NewExportHandler(
	exportService *services.ExportService,
	configService services.ConfigurationGetter,
	projectService *services.ProjectService,
) *ExportHandler {
	return &ExportHandler{
		exportService:  exportService,
		configService:  configService,
		projectService: projectService,
	}
}

type ExportRequest struct {
	Name        string `json:"name" binding:"required"`
	ProjectName string `json:"project_name"`
	ProjectUUID string `json:"project_uuid"`
	Article     string `json:"article"`
	ServerCount int    `json:"server_count"`
	PricelistID *uint  `json:"pricelist_id"`
	Items       []struct {
		LotName   string  `json:"lot_name" binding:"required"`
		Quantity  int     `json:"quantity" binding:"required,min=1"`
		UnitPrice float64 `json:"unit_price"`
	} `json:"items" binding:"required,min=1"`
	Notes string `json:"notes"`
}

func (h *ExportHandler) ExportCSV(c *gin.Context) {
	var req ExportRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	data := h.buildExportData(&req)

	// Validate before streaming (can return JSON error)
	if len(data.Configs) == 0 || len(data.Configs[0].Items) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no items to export"})
		return
	}

	// Get project code for filename
	projectCode := req.ProjectName // legacy field: may contain code from frontend
	if projectCode == "" && req.ProjectUUID != "" {
		username := middleware.GetUsername(c)
		if project, err := h.projectService.GetByUUID(req.ProjectUUID, username); err == nil && project != nil {
			projectCode = project.Code
		}
	}
	if projectCode == "" {
		projectCode = req.Name
	}

	// Set headers before streaming
	exportDate := data.CreatedAt
	articleSegment := sanitizeFilenameSegment(req.Article)
	if articleSegment == "" {
		articleSegment = "BOM"
	}
	filename := fmt.Sprintf("%s (%s) %s %s.csv", exportDate.Format("2006-01-02"), projectCode, req.Name, articleSegment)
	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))

	// Stream CSV (cannot return JSON after this point)
	if err := h.exportService.ToCSV(c.Writer, data); err != nil {
		c.Error(err) // Log only
		return
	}
}

// buildExportData converts an ExportRequest into a ProjectExportData using a temporary Configuration model
// so that ExportService.ConfigToExportData can resolve categories via localDB.
func (h *ExportHandler) buildExportData(req *ExportRequest) *services.ProjectExportData {
	configItems := make(models.ConfigItems, len(req.Items))
	for i, item := range req.Items {
		configItems[i] = models.ConfigItem{
			LotName:   item.LotName,
			Quantity:  item.Quantity,
			UnitPrice: item.UnitPrice,
		}
	}

	serverCount := req.ServerCount
	if serverCount < 1 {
		serverCount = 1
	}

	cfg := &models.Configuration{
		Article:     req.Article,
		ServerCount: serverCount,
		PricelistID: req.PricelistID,
		Items:       configItems,
		CreatedAt:   time.Now(),
	}

	return h.exportService.ConfigToExportData(cfg)
}

func sanitizeFilenameSegment(value string) string {
	if strings.TrimSpace(value) == "" {
		return ""
	}
	replacer := strings.NewReplacer(
		"/", "_",
		"\\", "_",
		":", "_",
		"*", "_",
		"?", "_",
		"\"", "_",
		"<", "_",
		">", "_",
		"|", "_",
	)
	return strings.TrimSpace(replacer.Replace(value))
}

func (h *ExportHandler) ExportConfigCSV(c *gin.Context) {
	username := middleware.GetUsername(c)
	uuid := c.Param("uuid")

	// Get config before streaming (can return JSON error)
	config, err := h.configService.GetByUUID(uuid, username)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}

	data := h.exportService.ConfigToExportData(config)

	// Validate before streaming (can return JSON error)
	if len(data.Configs) == 0 || len(data.Configs[0].Items) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no items to export"})
		return
	}

	// Get project code for filename
	projectCode := config.Name // fallback: use config name if no project
	if config.ProjectUUID != nil && *config.ProjectUUID != "" {
		if project, err := h.projectService.GetByUUID(*config.ProjectUUID, username); err == nil && project != nil {
			projectCode = project.Code
		}
	}

	// Set headers before streaming
	// Use price update time if available, otherwise creation time
	exportDate := config.CreatedAt
	if config.PriceUpdatedAt != nil {
		exportDate = *config.PriceUpdatedAt
	}
	filename := fmt.Sprintf("%s (%s) %s BOM.csv", exportDate.Format("2006-01-02"), projectCode, config.Name)
	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))

	// Stream CSV (cannot return JSON after this point)
	if err := h.exportService.ToCSV(c.Writer, data); err != nil {
		c.Error(err) // Log only
		return
	}
}

// ExportProjectCSV exports all active configurations of a project as a single CSV file.
func (h *ExportHandler) ExportProjectCSV(c *gin.Context) {
	username := middleware.GetUsername(c)
	projectUUID := c.Param("uuid")

	project, err := h.projectService.GetByUUID(projectUUID, username)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}

	result, err := h.projectService.ListConfigurations(projectUUID, username, "active")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if len(result.Configs) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no configurations to export"})
		return
	}

	data := h.exportService.ProjectToExportData(result.Configs)

	// Filename: YYYY-MM-DD (ProjectCode) BOM.csv
	filename := fmt.Sprintf("%s (%s) BOM.csv", time.Now().Format("2006-01-02"), project.Code)
	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))

	if err := h.exportService.ToCSV(c.Writer, data); err != nil {
		c.Error(err)
		return
	}
}
