package handlers

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

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

// ============================================================================
// Project Management
// ============================================================================

// CreateProject handles POST /projects
// Creates a new IACE compliance project for a machine or system.
func (h *IACEHandler) CreateProject(c *gin.Context) {
	tenantID, err := getTenantID(c)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	var req iace.CreateProjectRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	project, err := h.store.CreateProject(c.Request.Context(), tenantID, req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{"project": project})
}

// ListProjects handles GET /projects
// Lists all IACE projects for the authenticated tenant.
func (h *IACEHandler) ListProjects(c *gin.Context) {
	tenantID, err := getTenantID(c)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	projects, err := h.store.ListProjects(c.Request.Context(), tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if projects == nil {
		projects = []iace.Project{}
	}

	c.JSON(http.StatusOK, iace.ProjectListResponse{
		Projects: projects,
		Total:    len(projects),
	})
}

// GetProject handles GET /projects/:id
// Returns a project with its components, classifications, and completeness gates.
func (h *IACEHandler) GetProject(c *gin.Context) {
	projectID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	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
	}

	components, _ := h.store.ListComponents(c.Request.Context(), projectID)
	classifications, _ := h.store.GetClassifications(c.Request.Context(), projectID)

	if components == nil {
		components = []iace.Component{}
	}
	if classifications == nil {
		classifications = []iace.RegulatoryClassification{}
	}

	// Build completeness context to compute gates
	ctx := h.buildCompletenessContext(c, project, components, classifications)
	result := h.checker.Check(ctx)

	c.JSON(http.StatusOK, iace.ProjectDetailResponse{
		Project:           *project,
		Components:        components,
		Classifications:   classifications,
		CompletenessGates: result.Gates,
	})
}

// UpdateProject handles PUT /projects/:id
// Partially updates a project's mutable fields.
func (h *IACEHandler) UpdateProject(c *gin.Context) {
	projectID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	var req iace.UpdateProjectRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	project, err := h.store.UpdateProject(c.Request.Context(), projectID, req)
	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
	}

	c.JSON(http.StatusOK, gin.H{"project": project})
}

// ArchiveProject handles DELETE /projects/:id
// Archives a project by setting its status to archived.
func (h *IACEHandler) ArchiveProject(c *gin.Context) {
	projectID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	if err := h.store.ArchiveProject(c.Request.Context(), projectID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "project archived"})
}

// ============================================================================
// Onboarding
// ============================================================================

// InitFromProfile handles POST /projects/:id/init-from-profile
// Initializes a project from a company profile and compliance scope JSON payload.
func (h *IACEHandler) InitFromProfile(c *gin.Context) {
	projectID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	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
	}

	var req iace.InitFromProfileRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Parse compliance_scope to extract machine data
	var scope struct {
		MachineName           string   `json:"machine_name"`
		MachineType           string   `json:"machine_type"`
		IntendedUse           string   `json:"intended_use"`
		HasSoftware           bool     `json:"has_software"`
		HasFirmware           bool     `json:"has_firmware"`
		HasAI                 bool     `json:"has_ai"`
		IsNetworked           bool     `json:"is_networked"`
		ApplicableRegulations []string `json:"applicable_regulations"`
	}
	_ = json.Unmarshal(req.ComplianceScope, &scope)

	// Parse company_profile to extract manufacturer
	var profile struct {
		CompanyName  string `json:"company_name"`
		ContactName  string `json:"contact_name"`
		ContactEmail string `json:"contact_email"`
		Address      string `json:"address"`
	}
	_ = json.Unmarshal(req.CompanyProfile, &profile)

	// Store the profile and scope in project metadata
	profileData := map[string]json.RawMessage{
		"company_profile":  req.CompanyProfile,
		"compliance_scope": req.ComplianceScope,
	}
	metadataBytes, _ := json.Marshal(profileData)
	metadataRaw := json.RawMessage(metadataBytes)

	// Build update request — fill project fields from scope/profile
	updateReq := iace.UpdateProjectRequest{
		Metadata: &metadataRaw,
	}
	if scope.MachineName != "" {
		updateReq.MachineName = &scope.MachineName
	}
	if scope.MachineType != "" {
		updateReq.MachineType = &scope.MachineType
	}
	if scope.IntendedUse != "" {
		updateReq.Description = &scope.IntendedUse
	}
	if profile.CompanyName != "" {
		updateReq.Manufacturer = &profile.CompanyName
	}

	project, err = h.store.UpdateProject(c.Request.Context(), projectID, updateReq)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	ctx := c.Request.Context()

	// Create initial components from scope
	var createdComponents []iace.Component
	if scope.HasSoftware {
		comp, err := h.store.CreateComponent(ctx, iace.CreateComponentRequest{
			ProjectID: projectID, Name: "Software", ComponentType: iace.ComponentTypeSoftware,
			IsSafetyRelevant: true, IsNetworked: scope.IsNetworked,
		})
		if err == nil {
			createdComponents = append(createdComponents, *comp)
		}
	}
	if scope.HasFirmware {
		comp, err := h.store.CreateComponent(ctx, iace.CreateComponentRequest{
			ProjectID: projectID, Name: "Firmware", ComponentType: iace.ComponentTypeFirmware,
			IsSafetyRelevant: true,
		})
		if err == nil {
			createdComponents = append(createdComponents, *comp)
		}
	}
	if scope.HasAI {
		comp, err := h.store.CreateComponent(ctx, iace.CreateComponentRequest{
			ProjectID: projectID, Name: "KI-Modell", ComponentType: iace.ComponentTypeAIModel,
			IsSafetyRelevant: true, IsNetworked: scope.IsNetworked,
		})
		if err == nil {
			createdComponents = append(createdComponents, *comp)
		}
	}
	if scope.IsNetworked {
		comp, err := h.store.CreateComponent(ctx, iace.CreateComponentRequest{
			ProjectID: projectID, Name: "Netzwerk-Schnittstelle", ComponentType: iace.ComponentTypeNetwork,
			IsSafetyRelevant: false, IsNetworked: true,
		})
		if err == nil {
			createdComponents = append(createdComponents, *comp)
		}
	}

	// Trigger initial classifications for applicable regulations
	regulationMap := map[string]iace.RegulationType{
		"machinery_regulation": iace.RegulationMachineryRegulation,
		"ai_act":               iace.RegulationAIAct,
		"cra":                  iace.RegulationCRA,
		"nis2":                 iace.RegulationNIS2,
	}
	var triggeredRegulations []string
	for _, regStr := range scope.ApplicableRegulations {
		if regType, ok := regulationMap[regStr]; ok {
			triggeredRegulations = append(triggeredRegulations, regStr)
			// Create initial classification entry
			h.store.UpsertClassification(ctx, projectID, regType, "pending", "medium", 0.5, "Initialisiert aus Compliance-Scope", nil, nil)
		}
	}

	// Advance project status to onboarding
	if err := h.store.UpdateProjectStatus(ctx, projectID, iace.ProjectStatusOnboarding); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Add audit trail entry
	userID := rbac.GetUserID(c)
	h.store.AddAuditEntry(
		ctx, projectID, "project", projectID,
		iace.AuditActionUpdate, userID.String(), nil, metadataBytes,
	)

	c.JSON(http.StatusOK, gin.H{
		"message":               "project initialized from profile",
		"project":               project,
		"components_created":    len(createdComponents),
		"regulations_triggered": triggeredRegulations,
	})
}

// ============================================================================
// Variant Management
// ============================================================================

// CreateVariant handles POST /projects/:id/variants
// Creates a new variant sub-project linked to the given base project.
func (h *IACEHandler) CreateVariant(c *gin.Context) {
	parentID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	// Verify parent project exists
	parent, err := h.store.GetProject(c.Request.Context(), parentID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if parent == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "parent project not found"})
		return
	}

	var req iace.CreateProjectRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Force the parent link
	req.ParentProjectID = &parentID

	project, err := h.store.CreateProject(c.Request.Context(), parent.TenantID, req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{"project": project})
}

// ListVariants handles GET /projects/:id/variants
// Returns all variant sub-projects for a given base project.
func (h *IACEHandler) ListVariants(c *gin.Context) {
	parentID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	variants, err := h.store.ListVariants(c.Request.Context(), parentID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if variants == nil {
		variants = []iace.Project{}
	}

	c.JSON(http.StatusOK, iace.ProjectListResponse{
		Projects: variants,
		Total:    len(variants),
	})
}

// GetVariantGap handles GET /projects/:id/variant-gap
// Returns a gap analysis comparing a variant against its base project.
func (h *IACEHandler) GetVariantGap(c *gin.Context) {
	variantID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
		return
	}

	gap, err := h.store.GetVariantGap(c.Request.Context(), variantID)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gap)
}
