package handlers

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

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

// ============================================================================
// Norms Library & Norm Suggestions
// ============================================================================

// ListNormsLibrary handles GET /norms-library
// Returns the full norms library, optionally filtered by ?type=A|B1|B2|C
// and ?hazard_category=xxx.
func (h *IACEHandler) ListNormsLibrary(c *gin.Context) {
	normType := c.Query("type")
	hazardCat := c.Query("hazard_category")

	allNorms := iace.GetNormsLibrary()
	allNorms = append(allNorms, iace.GetExtendedB2Norms()...)
	allNorms = append(allNorms, iace.GetCNormsLibrary()...)
	allNorms = append(allNorms, iace.GetExtendedCNormsLibrary()...)
	allNorms = append(allNorms, iace.GetWoodMetalCNorms()...)
	allNorms = append(allNorms, iace.GetFoodPkgCNorms()...)
	allNorms = append(allNorms, iace.GetLiftMiscCNorms()...)
	allNorms = append(allNorms, iace.GetMachiningCNorms()...)
	allNorms = append(allNorms, iace.GetConveyorAutoCNorms()...)
	allNorms = append(allNorms, iace.GetProcessCNorms()...)
	allNorms = append(allNorms, iace.GetConstructionCNorms()...)
	allNorms = append(allNorms, iace.GetNiche1CNorms()...)
	allNorms = append(allNorms, iace.GetNiche2CNorms()...)
	allNorms = append(allNorms, iace.GetNiche3CNorms()...)
	allNorms = append(allNorms, iace.GetExtendedB2Norms2()...)
	allNorms = append(allNorms, iace.GetWave3aCNorms()...)
	allNorms = append(allNorms, iace.GetWave3a2CNorms()...)
	allNorms = append(allNorms, iace.GetWave3bCNorms()...)
	allNorms = append(allNorms, iace.GetWave3cCNorms()...)
	allNorms = append(allNorms, iace.GetWave3c2CNorms()...)
	allNorms = append(allNorms, iace.GetWave3dCNorms()...)
	allNorms = append(allNorms, iace.GetWave3dExtCNorms()...)
	allNorms = append(allNorms, iace.GetWave3dHvacCNorms()...)
	allNorms = append(allNorms, iace.GetFinalCNorms()...)

	includeCrossRef := c.Query("include_crossref") == "true"

	var filtered []iace.NormReference
	for _, norm := range allNorms {
		if normType != "" && norm.NormType != normType {
			continue
		}
		if hazardCat != "" && !containsString(norm.HazardCats, hazardCat) {
			continue
		}
		if includeCrossRef {
			cr := iace.GetNormCrossRef(norm.ID)
			if len(cr.Mappings) > 0 {
				norm.CrossRef = &cr
			}
		}
		filtered = append(filtered, norm)
	}

	if filtered == nil {
		filtered = []iace.NormReference{}
	}

	covered, total := iace.CrossRefCoverage(len(allNorms))
	c.JSON(http.StatusOK, gin.H{
		"norms": filtered,
		"total": len(filtered),
		"crossref_coverage": gin.H{
			"covered":     covered,
			"total_norms": total,
		},
	})
}

// GetNormCrossRef handles GET /norms-library/:id/crossref
// Returns the international cross-reference (DIN/ANSI/GB/JIS/...) for a single norm.
func (h *IACEHandler) GetNormCrossRef(c *gin.Context) {
	normID := c.Param("id")
	if normID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "norm id required"})
		return
	}
	cr := iace.GetNormCrossRef(normID)
	c.JSON(http.StatusOK, cr)
}

// ListNormCrossRefs handles GET /norms-library/crossref
// Returns the entire cross-reference matrix (all populated entries).
func (h *IACEHandler) ListNormCrossRefs(c *gin.Context) {
	entries := iace.ListNormCrossRefs()
	c.JSON(http.StatusOK, gin.H{
		"entries": entries,
		"total":   len(entries),
	})
}

// SuggestProjectNorms handles GET /projects/:id/suggested-norms
// Returns norm suggestions based on the project's machine type, identified
// hazards, and component tags.
func (h *IACEHandler) SuggestProjectNorms(c *gin.Context) {
	projectID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	// Fetch project to get machine type
	project, err := h.store.GetProject(c.Request.Context(), projectID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if project == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "project not found"})
		return
	}

	// Collect unique hazard categories from project hazards
	hazardCategories := collectHazardCategories(h, c, projectID)

	// Collect tags from component metadata
	tags := collectComponentTags(h, c, projectID)

	result := iace.SuggestNorms(project.MachineType, hazardCategories, tags)

	c.JSON(http.StatusOK, gin.H{
		"suggestions":       result,
		"machine_type":      project.MachineType,
		"hazard_categories": hazardCategories,
		"tags":              tags,
	})
}

// collectHazardCategories extracts unique hazard categories from a project's hazards.
func collectHazardCategories(h *IACEHandler, c *gin.Context, projectID uuid.UUID) []string {
	hazards, err := h.store.ListHazards(c.Request.Context(), projectID)
	if err != nil {
		return []string{}
	}

	seen := make(map[string]bool)
	var categories []string
	for _, hz := range hazards {
		if hz.Category != "" && !seen[hz.Category] {
			seen[hz.Category] = true
			categories = append(categories, hz.Category)
		}
	}
	return categories
}

// collectComponentTags extracts tags from the metadata JSON of project components.
// Components store tags in their metadata field as {"tags": ["tag1", "tag2"]}.
// Additionally, component types are mapped to tags via the component library.
func collectComponentTags(h *IACEHandler, c *gin.Context, projectID uuid.UUID) []string {
	components, err := h.store.ListComponents(c.Request.Context(), projectID)
	if err != nil {
		return []string{}
	}

	seen := make(map[string]bool)
	var tags []string

	for _, comp := range components {
		// Extract tags from metadata JSON
		extracted := extractTagsFromMetadata(comp.Metadata)
		for _, t := range extracted {
			if !seen[t] {
				seen[t] = true
				tags = append(tags, t)
			}
		}

		// Extract tags from description field ("Tags: x, y, z" pattern)
		descTags := parseDescriptionTags(comp.Description)
		for _, t := range descTags {
			if !seen[t] {
				seen[t] = true
				tags = append(tags, t)
			}
		}
	}

	return tags
}

// extractTagsFromMetadata parses the component metadata JSON for a "tags" array.
func extractTagsFromMetadata(metadata json.RawMessage) []string {
	if len(metadata) == 0 {
		return nil
	}

	var m map[string]interface{}
	if err := json.Unmarshal(metadata, &m); err != nil {
		return nil
	}

	tagsRaw, ok := m["tags"]
	if !ok {
		return nil
	}

	arr, ok := tagsRaw.([]interface{})
	if !ok {
		return nil
	}

	var tags []string
	for _, v := range arr {
		if s, ok := v.(string); ok && s != "" {
			tags = append(tags, s)
		}
	}
	return tags
}

// parseDescriptionTags looks for a "Tags: x, y, z" pattern in the description.
func parseDescriptionTags(description string) []string {
	idx := strings.Index(strings.ToLower(description), "tags:")
	if idx < 0 {
		return nil
	}

	// Take everything after "Tags:"
	rest := strings.TrimSpace(description[idx+5:])
	if rest == "" {
		return nil
	}

	// Split by comma and trim each tag
	parts := strings.Split(rest, ",")
	var tags []string
	for _, p := range parts {
		t := strings.TrimSpace(p)
		if t != "" {
			tags = append(tags, t)
		}
	}
	return tags
}
