package handlers

import (
	"net/http"
	"strconv"

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

// PortfolioHandlers handles portfolio HTTP requests
type PortfolioHandlers struct {
	store *portfolio.Store
}

// NewPortfolioHandlers creates new portfolio handlers
func NewPortfolioHandlers(store *portfolio.Store) *PortfolioHandlers {
	return &PortfolioHandlers{store: store}
}

// ============================================================================
// Portfolio CRUD
// ============================================================================

// CreatePortfolio creates a new portfolio
// POST /sdk/v1/portfolios
func (h *PortfolioHandlers) CreatePortfolio(c *gin.Context) {
	var req portfolio.CreatePortfolioRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)

	p := &portfolio.Portfolio{
		TenantID:     tenantID,
		Name:         req.Name,
		Description:  req.Description,
		Status:       portfolio.PortfolioStatusDraft,
		Department:   req.Department,
		BusinessUnit: req.BusinessUnit,
		Owner:        req.Owner,
		OwnerEmail:   req.OwnerEmail,
		Settings:     req.Settings,
		CreatedBy:    userID,
	}

	// Set default settings
	if !p.Settings.AutoUpdateMetrics {
		p.Settings.AutoUpdateMetrics = true
	}

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

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

// ListPortfolios lists portfolios
// GET /sdk/v1/portfolios
func (h *PortfolioHandlers) ListPortfolios(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)

	filters := &portfolio.PortfolioFilters{
		Limit: 50,
	}

	if status := c.Query("status"); status != "" {
		filters.Status = portfolio.PortfolioStatus(status)
	}
	if department := c.Query("department"); department != "" {
		filters.Department = department
	}
	if businessUnit := c.Query("business_unit"); businessUnit != "" {
		filters.BusinessUnit = businessUnit
	}
	if owner := c.Query("owner"); owner != "" {
		filters.Owner = owner
	}
	if limit := c.Query("limit"); limit != "" {
		if l, err := strconv.Atoi(limit); err == nil {
			filters.Limit = l
		}
	}
	if offset := c.Query("offset"); offset != "" {
		if o, err := strconv.Atoi(offset); err == nil {
			filters.Offset = o
		}
	}

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

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

// GetPortfolio retrieves a portfolio
// GET /sdk/v1/portfolios/:id
func (h *PortfolioHandlers) GetPortfolio(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

	summary, err := h.store.GetPortfolioSummary(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if summary == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "portfolio not found"})
		return
	}

	// Get stats
	stats, _ := h.store.GetPortfolioStats(c.Request.Context(), id)

	c.JSON(http.StatusOK, gin.H{
		"portfolio":         summary.Portfolio,
		"items":             summary.Items,
		"risk_distribution": summary.RiskDistribution,
		"feasibility_dist":  summary.FeasibilityDist,
		"stats":             stats,
	})
}

// UpdatePortfolio updates a portfolio
// PUT /sdk/v1/portfolios/:id
func (h *PortfolioHandlers) UpdatePortfolio(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

	p, err := h.store.GetPortfolio(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if p == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "portfolio not found"})
		return
	}

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

	if req.Name != "" {
		p.Name = req.Name
	}
	if req.Description != "" {
		p.Description = req.Description
	}
	if req.Status != "" {
		p.Status = req.Status
	}
	if req.Department != "" {
		p.Department = req.Department
	}
	if req.BusinessUnit != "" {
		p.BusinessUnit = req.BusinessUnit
	}
	if req.Owner != "" {
		p.Owner = req.Owner
	}
	if req.OwnerEmail != "" {
		p.OwnerEmail = req.OwnerEmail
	}
	if req.Settings != nil {
		p.Settings = *req.Settings
	}

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

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

// DeletePortfolio deletes a portfolio
// DELETE /sdk/v1/portfolios/:id
func (h *PortfolioHandlers) DeletePortfolio(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

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

	c.JSON(http.StatusOK, gin.H{"message": "portfolio deleted"})
}

// ============================================================================
// Portfolio Items
// ============================================================================

// AddItem adds an item to a portfolio
// POST /sdk/v1/portfolios/:id/items
func (h *PortfolioHandlers) AddItem(c *gin.Context) {
	portfolioID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

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

	userID := rbac.GetUserID(c)

	item := &portfolio.PortfolioItem{
		PortfolioID: portfolioID,
		ItemType:    req.ItemType,
		ItemID:      req.ItemID,
		Tags:        req.Tags,
		Notes:       req.Notes,
		AddedBy:     userID,
	}

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

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

// ListItems lists items in a portfolio
// GET /sdk/v1/portfolios/:id/items
func (h *PortfolioHandlers) ListItems(c *gin.Context) {
	portfolioID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

	var itemType *portfolio.ItemType
	if t := c.Query("type"); t != "" {
		it := portfolio.ItemType(t)
		itemType = &it
	}

	items, err := h.store.ListItems(c.Request.Context(), portfolioID, itemType)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

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

// BulkAddItems adds multiple items to a portfolio
// POST /sdk/v1/portfolios/:id/items/bulk
func (h *PortfolioHandlers) BulkAddItems(c *gin.Context) {
	portfolioID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

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

	userID := rbac.GetUserID(c)

	// Convert AddItemRequest to PortfolioItem
	items := make([]portfolio.PortfolioItem, len(req.Items))
	for i, r := range req.Items {
		items[i] = portfolio.PortfolioItem{
			ItemType: r.ItemType,
			ItemID:   r.ItemID,
			Tags:     r.Tags,
			Notes:    r.Notes,
		}
	}

	result, err := h.store.BulkAddItems(c.Request.Context(), portfolioID, items, userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, result)
}

// RemoveItem removes an item from a portfolio
// DELETE /sdk/v1/portfolios/:id/items/:itemId
func (h *PortfolioHandlers) RemoveItem(c *gin.Context) {
	itemID, err := uuid.Parse(c.Param("itemId"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid item ID"})
		return
	}

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

	c.JSON(http.StatusOK, gin.H{"message": "item removed"})
}

// ReorderItems updates the order of items
// PUT /sdk/v1/portfolios/:id/items/order
func (h *PortfolioHandlers) ReorderItems(c *gin.Context) {
	portfolioID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

	var req struct {
		ItemIDs []uuid.UUID `json:"item_ids"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

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

	c.JSON(http.StatusOK, gin.H{"message": "items reordered"})
}

// ============================================================================
// Merge Operations
// ============================================================================

// MergePortfolios merges two portfolios
// POST /sdk/v1/portfolios/merge
func (h *PortfolioHandlers) MergePortfolios(c *gin.Context) {
	var req portfolio.MergeRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Validate portfolios exist
	source, err := h.store.GetPortfolio(c.Request.Context(), req.SourcePortfolioID)
	if err != nil || source == nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "source portfolio not found"})
		return
	}

	target, err := h.store.GetPortfolio(c.Request.Context(), req.TargetPortfolioID)
	if err != nil || target == nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "target portfolio not found"})
		return
	}

	// Set defaults
	if req.Strategy == "" {
		req.Strategy = portfolio.MergeStrategyUnion
	}

	userID := rbac.GetUserID(c)

	result, err := h.store.MergePortfolios(c.Request.Context(), &req, userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "portfolios merged",
		"result":  result,
	})
}

// ============================================================================
// Statistics & Reports
// ============================================================================

// GetPortfolioStats returns statistics for a portfolio
// GET /sdk/v1/portfolios/:id/stats
func (h *PortfolioHandlers) GetPortfolioStats(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

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

	c.JSON(http.StatusOK, stats)
}

// GetPortfolioActivity returns recent activity for a portfolio
// GET /sdk/v1/portfolios/:id/activity
func (h *PortfolioHandlers) GetPortfolioActivity(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

	limit := 20
	if l := c.Query("limit"); l != "" {
		if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 {
			limit = parsed
		}
	}

	activities, err := h.store.GetRecentActivity(c.Request.Context(), id, limit)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

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

// ComparePortfolios compares multiple portfolios
// POST /sdk/v1/portfolios/compare
func (h *PortfolioHandlers) ComparePortfolios(c *gin.Context) {
	var req portfolio.ComparePortfoliosRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if len(req.PortfolioIDs) < 2 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "at least 2 portfolios required for comparison"})
		return
	}
	if len(req.PortfolioIDs) > 5 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "maximum 5 portfolios can be compared"})
		return
	}

	// Get all portfolios
	var portfolios []portfolio.Portfolio
	comparison := portfolio.PortfolioComparison{
		RiskScores:       make(map[string]float64),
		ComplianceScores: make(map[string]float64),
		ItemCounts:       make(map[string]int),
		UniqueItems:      make(map[string][]uuid.UUID),
	}

	allItems := make(map[uuid.UUID][]string) // item_id -> portfolio_ids

	for _, id := range req.PortfolioIDs {
		p, err := h.store.GetPortfolio(c.Request.Context(), id)
		if err != nil || p == nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "portfolio not found: " + id.String()})
			return
		}
		portfolios = append(portfolios, *p)

		idStr := id.String()
		comparison.RiskScores[idStr] = p.AvgRiskScore
		comparison.ComplianceScores[idStr] = p.ComplianceScore
		comparison.ItemCounts[idStr] = p.TotalAssessments + p.TotalRoadmaps + p.TotalWorkshops

		// Get items for comparison
		items, _ := h.store.ListItems(c.Request.Context(), id, nil)
		for _, item := range items {
			allItems[item.ItemID] = append(allItems[item.ItemID], idStr)
		}
	}

	// Find common and unique items
	for itemID, portfolioIDs := range allItems {
		if len(portfolioIDs) > 1 {
			comparison.CommonItems = append(comparison.CommonItems, itemID)
		} else {
			pid := portfolioIDs[0]
			comparison.UniqueItems[pid] = append(comparison.UniqueItems[pid], itemID)
		}
	}

	c.JSON(http.StatusOK, portfolio.ComparePortfoliosResponse{
		Portfolios: portfolios,
		Comparison: comparison,
	})
}

// RecalculateMetrics manually recalculates portfolio metrics
// POST /sdk/v1/portfolios/:id/recalculate
func (h *PortfolioHandlers) RecalculateMetrics(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

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

	// Get updated portfolio
	p, _ := h.store.GetPortfolio(c.Request.Context(), id)

	c.JSON(http.StatusOK, gin.H{
		"message":   "metrics recalculated",
		"portfolio": p,
	})
}

// ============================================================================
// Approval Workflow
// ============================================================================

// ApprovePortfolio approves a portfolio
// POST /sdk/v1/portfolios/:id/approve
func (h *PortfolioHandlers) ApprovePortfolio(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

	p, err := h.store.GetPortfolio(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if p == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "portfolio not found"})
		return
	}

	if p.Status != portfolio.PortfolioStatusReview {
		c.JSON(http.StatusBadRequest, gin.H{"error": "portfolio must be in REVIEW status to approve"})
		return
	}

	userID := rbac.GetUserID(c)
	now := c.Request.Context().Value("now")
	if now == nil {
		t := p.UpdatedAt
		p.ApprovedAt = &t
	}
	p.ApprovedBy = &userID
	p.Status = portfolio.PortfolioStatusApproved

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

	c.JSON(http.StatusOK, gin.H{
		"message":   "portfolio approved",
		"portfolio": p,
	})
}

// SubmitForReview submits a portfolio for review
// POST /sdk/v1/portfolios/:id/submit-review
func (h *PortfolioHandlers) SubmitForReview(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
		return
	}

	p, err := h.store.GetPortfolio(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if p == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "portfolio not found"})
		return
	}

	if p.Status != portfolio.PortfolioStatusDraft && p.Status != portfolio.PortfolioStatusActive {
		c.JSON(http.StatusBadRequest, gin.H{"error": "portfolio must be in DRAFT or ACTIVE status to submit for review"})
		return
	}

	p.Status = portfolio.PortfolioStatusReview

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

	c.JSON(http.StatusOK, gin.H{
		"message":   "portfolio submitted for review",
		"portfolio": p,
	})
}
