package handlers

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/breakpilot/ai-compliance-sdk/internal/iace"
	"github.com/breakpilot/ai-compliance-sdk/internal/llm"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// ExportFMEA handles GET /projects/:id/fmea/export
// Returns an xlsx file in VDA FMEA format.
func (h *IACEHandler) ExportFMEA(c *gin.Context) {
	projectID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	ctx := c.Request.Context()
	project, err := h.store.GetProject(ctx, projectID)
	if err != nil || project == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "project not found"})
		return
	}

	// Load components
	components, _ := h.store.ListComponents(ctx, projectID)

	// Load all failure modes
	allFMs := iace.GetFailureModeLibrary()

	// Build FMEA rows: each component × matching FMs
	var rows []iace.FMEAExportRow
	for _, comp := range components {
		compType := string(comp.ComponentType)
		var compFMs []iace.FailureModeEntry
		for _, fm := range allFMs {
			if fm.ComponentType == compType {
				compFMs = append(compFMs, fm)
			}
		}
		if len(compFMs) == 0 {
			// Fallback: mechanical FMs
			for _, fm := range allFMs {
				if fm.ComponentType == "mechanical" && len(compFMs) < 3 {
					compFMs = append(compFMs, fm)
				}
			}
		}

		for _, fm := range compFMs {
			s, o, d := fm.DefaultSeverity, fm.DefaultOccurrence, fm.DefaultDetection
			rows = append(rows, iace.FMEAExportRow{
				ComponentName: comp.Name,
				ComponentType: compType,
				FailureMode:   fm.NameDE,
				FailureEffect: fm.Effect,
				FailureCause:  fm.DetectionHint,
				Severity:      s,
				Occurrence:    o,
				Detection:     d,
				RPZ:           s * o * d,
				AP:            iace.CalculateAP(s, o, d),
				Measure:       "",
				DetectionHint: fm.DetectionHint,
			})
		}
	}

	xlsxBytes, err := iace.GenerateFMEAExcel(project.MachineName, rows)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Excel generation failed: %v", err)})
		return
	}

	filename := fmt.Sprintf("FMEA-%s.xlsx", project.MachineName)
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
	c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", xlsxBytes)
}

// SuggestFailureModes handles POST /projects/:id/components/:cid/suggest-fms
// Uses LLM to suggest failure modes for a specific component.
func (h *IACEHandler) SuggestFailureModes(c *gin.Context) {
	projectID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}
	componentID, err := uuid.Parse(c.Param("cid"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid component ID"})
		return
	}

	ctx := c.Request.Context()
	project, err := h.store.GetProject(ctx, projectID)
	if err != nil || project == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "project not found"})
		return
	}

	comp, err := h.store.GetComponent(ctx, componentID)
	if err != nil || comp == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "component not found"})
		return
	}

	// Build LLM prompt
	prompt := fmt.Sprintf(
		`Du bist ein FMEA-Experte (Fehlermoeglich- und Einflussanalyse) nach AIAG-VDA.
Fuer die Komponente "%s" (Typ: %s) in der Maschine "%s" (%s):

Nenne die 5 wichtigsten Failure Modes. Fuer jeden:
- mode: Kurzbezeichnung der Fehlerart
- name_de: Deutsche Beschreibung
- effect: Systemauswirkung
- severity: Schwere 1-10 (10=katastrophal)
- occurrence: Auftretenswahrscheinlichkeit 1-10 (10=sehr haeufig)
- detection: Entdeckbarkeit 1-10 (10=nicht erkennbar)

Antworte NUR mit einem JSON-Array, keine Erklaerungen:
[{"mode":"...","name_de":"...","effect":"...","severity":N,"occurrence":N,"detection":N}]`,
		comp.Name, comp.ComponentType, project.MachineName, project.MachineType)

	// Try LLM
	suggestions, err := callLLMForFMs(ctx, h.llmRegistry, prompt)
	if err != nil {
		// Fallback: return library FMs for this component type
		allFMs := iace.GetFailureModeLibrary()
		var fallback []iace.FailureModeEntry
		for _, fm := range allFMs {
			if fm.ComponentType == string(comp.ComponentType) && len(fallback) < 5 {
				fallback = append(fallback, fm)
			}
		}
		c.JSON(http.StatusOK, gin.H{
			"suggestions": fallback,
			"source":      "library_fallback",
			"total":       len(fallback),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"suggestions": suggestions,
		"source":      "llm",
		"total":       len(suggestions),
	})
}

func callLLMForFMs(ctx context.Context, registry *llm.ProviderRegistry, prompt string) ([]iace.FailureModeEntry, error) {
	if registry == nil {
		return nil, fmt.Errorf("no LLM registry")
	}

	provider, err := registry.GetAvailable(ctx)
	if err != nil {
		return nil, fmt.Errorf("no LLM provider available: %w", err)
	}

	resp, err := provider.Chat(ctx, &llm.ChatRequest{
		Messages: []llm.Message{
			{Role: "user", Content: prompt},
		},
		Temperature: 0.3,
		MaxTokens:   1000,
	})
	if err != nil {
		return nil, fmt.Errorf("LLM call failed: %w", err)
	}

	// Parse JSON from response
	content := strings.TrimSpace(resp.Message.Content)
	// Strip markdown code fences if present
	content = strings.TrimPrefix(content, "```json")
	content = strings.TrimPrefix(content, "```")
	content = strings.TrimSuffix(content, "```")
	content = strings.TrimSpace(content)

	var rawFMs []struct {
		Mode       string `json:"mode"`
		NameDE     string `json:"name_de"`
		Effect     string `json:"effect"`
		Severity   int    `json:"severity"`
		Occurrence int    `json:"occurrence"`
		Detection  int    `json:"detection"`
	}
	if err := json.Unmarshal([]byte(content), &rawFMs); err != nil {
		return nil, fmt.Errorf("failed to parse LLM response: %w", err)
	}

	var result []iace.FailureModeEntry
	for i, fm := range rawFMs {
		result = append(result, iace.FailureModeEntry{
			ID:                fmt.Sprintf("LLM-%03d", i+1),
			ComponentType:     "llm_suggested",
			Mode:              fm.Mode,
			NameDE:            fm.NameDE,
			Effect:            fm.Effect,
			DefaultSeverity:   clamp(fm.Severity, 1, 10),
			DefaultOccurrence: clamp(fm.Occurrence, 1, 10),
			DefaultDetection:  clamp(fm.Detection, 1, 10),
		})
	}
	return result, nil
}

func clamp(v, min, max int) int {
	if v < min {
		return min
	}
	if v > max {
		return max
	}
	return v
}
