package portfolio

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

// Store handles portfolio data persistence
type Store struct {
	pool *pgxpool.Pool
}

// NewStore creates a new portfolio store
func NewStore(pool *pgxpool.Pool) *Store {
	return &Store{pool: pool}
}

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

// CreatePortfolio creates a new portfolio
func (s *Store) CreatePortfolio(ctx context.Context, p *Portfolio) error {
	p.ID = uuid.New()
	p.CreatedAt = time.Now().UTC()
	p.UpdatedAt = p.CreatedAt
	if p.Status == "" {
		p.Status = PortfolioStatusDraft
	}

	settings, _ := json.Marshal(p.Settings)

	_, err := s.pool.Exec(ctx, `
		INSERT INTO portfolios (
			id, tenant_id, namespace_id,
			name, description, status,
			department, business_unit, owner, owner_email,
			total_assessments, total_roadmaps, total_workshops,
			avg_risk_score, high_risk_count, conditional_count, approved_count,
			compliance_score, settings,
			created_at, updated_at, created_by, approved_at, approved_by
		) VALUES (
			$1, $2, $3,
			$4, $5, $6,
			$7, $8, $9, $10,
			$11, $12, $13,
			$14, $15, $16, $17,
			$18, $19,
			$20, $21, $22, $23, $24
		)
	`,
		p.ID, p.TenantID, p.NamespaceID,
		p.Name, p.Description, string(p.Status),
		p.Department, p.BusinessUnit, p.Owner, p.OwnerEmail,
		p.TotalAssessments, p.TotalRoadmaps, p.TotalWorkshops,
		p.AvgRiskScore, p.HighRiskCount, p.ConditionalCount, p.ApprovedCount,
		p.ComplianceScore, settings,
		p.CreatedAt, p.UpdatedAt, p.CreatedBy, p.ApprovedAt, p.ApprovedBy,
	)

	return err
}

// GetPortfolio retrieves a portfolio by ID
func (s *Store) GetPortfolio(ctx context.Context, id uuid.UUID) (*Portfolio, error) {
	var p Portfolio
	var status string
	var settings []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, tenant_id, namespace_id,
			name, description, status,
			department, business_unit, owner, owner_email,
			total_assessments, total_roadmaps, total_workshops,
			avg_risk_score, high_risk_count, conditional_count, approved_count,
			compliance_score, settings,
			created_at, updated_at, created_by, approved_at, approved_by
		FROM portfolios WHERE id = $1
	`, id).Scan(
		&p.ID, &p.TenantID, &p.NamespaceID,
		&p.Name, &p.Description, &status,
		&p.Department, &p.BusinessUnit, &p.Owner, &p.OwnerEmail,
		&p.TotalAssessments, &p.TotalRoadmaps, &p.TotalWorkshops,
		&p.AvgRiskScore, &p.HighRiskCount, &p.ConditionalCount, &p.ApprovedCount,
		&p.ComplianceScore, &settings,
		&p.CreatedAt, &p.UpdatedAt, &p.CreatedBy, &p.ApprovedAt, &p.ApprovedBy,
	)

	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	p.Status = PortfolioStatus(status)
	json.Unmarshal(settings, &p.Settings)

	return &p, nil
}

// ListPortfolios lists portfolios for a tenant with optional filters
func (s *Store) ListPortfolios(ctx context.Context, tenantID uuid.UUID, filters *PortfolioFilters) ([]Portfolio, error) {
	query := `
		SELECT
			id, tenant_id, namespace_id,
			name, description, status,
			department, business_unit, owner, owner_email,
			total_assessments, total_roadmaps, total_workshops,
			avg_risk_score, high_risk_count, conditional_count, approved_count,
			compliance_score, settings,
			created_at, updated_at, created_by, approved_at, approved_by
		FROM portfolios WHERE tenant_id = $1`

	args := []interface{}{tenantID}
	argIdx := 2

	if filters != nil {
		if filters.Status != "" {
			query += fmt.Sprintf(" AND status = $%d", argIdx)
			args = append(args, string(filters.Status))
			argIdx++
		}
		if filters.Department != "" {
			query += fmt.Sprintf(" AND department = $%d", argIdx)
			args = append(args, filters.Department)
			argIdx++
		}
		if filters.BusinessUnit != "" {
			query += fmt.Sprintf(" AND business_unit = $%d", argIdx)
			args = append(args, filters.BusinessUnit)
			argIdx++
		}
		if filters.Owner != "" {
			query += fmt.Sprintf(" AND owner ILIKE $%d", argIdx)
			args = append(args, "%"+filters.Owner+"%")
			argIdx++
		}
		if filters.MinRiskScore != nil {
			query += fmt.Sprintf(" AND avg_risk_score >= $%d", argIdx)
			args = append(args, *filters.MinRiskScore)
			argIdx++
		}
		if filters.MaxRiskScore != nil {
			query += fmt.Sprintf(" AND avg_risk_score <= $%d", argIdx)
			args = append(args, *filters.MaxRiskScore)
			argIdx++
		}
	}

	query += " ORDER BY updated_at DESC"

	if filters != nil && filters.Limit > 0 {
		query += fmt.Sprintf(" LIMIT $%d", argIdx)
		args = append(args, filters.Limit)
		argIdx++

		if filters.Offset > 0 {
			query += fmt.Sprintf(" OFFSET $%d", argIdx)
			args = append(args, filters.Offset)
		}
	}

	rows, err := s.pool.Query(ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var portfolios []Portfolio
	for rows.Next() {
		var p Portfolio
		var status string
		var settings []byte

		err := rows.Scan(
			&p.ID, &p.TenantID, &p.NamespaceID,
			&p.Name, &p.Description, &status,
			&p.Department, &p.BusinessUnit, &p.Owner, &p.OwnerEmail,
			&p.TotalAssessments, &p.TotalRoadmaps, &p.TotalWorkshops,
			&p.AvgRiskScore, &p.HighRiskCount, &p.ConditionalCount, &p.ApprovedCount,
			&p.ComplianceScore, &settings,
			&p.CreatedAt, &p.UpdatedAt, &p.CreatedBy, &p.ApprovedAt, &p.ApprovedBy,
		)
		if err != nil {
			return nil, err
		}

		p.Status = PortfolioStatus(status)
		json.Unmarshal(settings, &p.Settings)

		portfolios = append(portfolios, p)
	}

	return portfolios, nil
}

// UpdatePortfolio updates a portfolio
func (s *Store) UpdatePortfolio(ctx context.Context, p *Portfolio) error {
	p.UpdatedAt = time.Now().UTC()

	settings, _ := json.Marshal(p.Settings)

	_, err := s.pool.Exec(ctx, `
		UPDATE portfolios SET
			name = $2, description = $3, status = $4,
			department = $5, business_unit = $6, owner = $7, owner_email = $8,
			settings = $9,
			updated_at = $10, approved_at = $11, approved_by = $12
		WHERE id = $1
	`,
		p.ID, p.Name, p.Description, string(p.Status),
		p.Department, p.BusinessUnit, p.Owner, p.OwnerEmail,
		settings,
		p.UpdatedAt, p.ApprovedAt, p.ApprovedBy,
	)

	return err
}

// DeletePortfolio deletes a portfolio and its items
func (s *Store) DeletePortfolio(ctx context.Context, id uuid.UUID) error {
	// Delete items first
	_, err := s.pool.Exec(ctx, "DELETE FROM portfolio_items WHERE portfolio_id = $1", id)
	if err != nil {
		return err
	}
	// Delete portfolio
	_, err = s.pool.Exec(ctx, "DELETE FROM portfolios WHERE id = $1", id)
	return err
}

// ============================================================================
// Portfolio Item Operations
// ============================================================================

// AddItem adds an item to a portfolio
func (s *Store) AddItem(ctx context.Context, item *PortfolioItem) error {
	item.ID = uuid.New()
	item.AddedAt = time.Now().UTC()

	tags, _ := json.Marshal(item.Tags)

	// Check if item already exists in portfolio
	var exists bool
	s.pool.QueryRow(ctx,
		"SELECT EXISTS(SELECT 1 FROM portfolio_items WHERE portfolio_id = $1 AND item_id = $2)",
		item.PortfolioID, item.ItemID).Scan(&exists)

	if exists {
		return fmt.Errorf("item already exists in portfolio")
	}

	// Get max sort order
	var maxSort int
	s.pool.QueryRow(ctx,
		"SELECT COALESCE(MAX(sort_order), 0) FROM portfolio_items WHERE portfolio_id = $1",
		item.PortfolioID).Scan(&maxSort)
	item.SortOrder = maxSort + 1

	_, err := s.pool.Exec(ctx, `
		INSERT INTO portfolio_items (
			id, portfolio_id, item_type, item_id,
			title, status, risk_level, risk_score, feasibility,
			sort_order, tags, notes,
			added_at, added_by
		) VALUES (
			$1, $2, $3, $4,
			$5, $6, $7, $8, $9,
			$10, $11, $12,
			$13, $14
		)
	`,
		item.ID, item.PortfolioID, string(item.ItemType), item.ItemID,
		item.Title, item.Status, item.RiskLevel, item.RiskScore, item.Feasibility,
		item.SortOrder, tags, item.Notes,
		item.AddedAt, item.AddedBy,
	)

	if err != nil {
		return err
	}

	// Update portfolio metrics
	return s.RecalculateMetrics(ctx, item.PortfolioID)
}

// GetItem retrieves a portfolio item by ID
func (s *Store) GetItem(ctx context.Context, id uuid.UUID) (*PortfolioItem, error) {
	var item PortfolioItem
	var itemType string
	var tags []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, portfolio_id, item_type, item_id,
			title, status, risk_level, risk_score, feasibility,
			sort_order, tags, notes,
			added_at, added_by
		FROM portfolio_items WHERE id = $1
	`, id).Scan(
		&item.ID, &item.PortfolioID, &itemType, &item.ItemID,
		&item.Title, &item.Status, &item.RiskLevel, &item.RiskScore, &item.Feasibility,
		&item.SortOrder, &tags, &item.Notes,
		&item.AddedAt, &item.AddedBy,
	)

	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	item.ItemType = ItemType(itemType)
	json.Unmarshal(tags, &item.Tags)

	return &item, nil
}

// ListItems lists items in a portfolio
func (s *Store) ListItems(ctx context.Context, portfolioID uuid.UUID, itemType *ItemType) ([]PortfolioItem, error) {
	query := `
		SELECT
			id, portfolio_id, item_type, item_id,
			title, status, risk_level, risk_score, feasibility,
			sort_order, tags, notes,
			added_at, added_by
		FROM portfolio_items WHERE portfolio_id = $1`

	args := []interface{}{portfolioID}
	if itemType != nil {
		query += " AND item_type = $2"
		args = append(args, string(*itemType))
	}

	query += " ORDER BY sort_order ASC"

	rows, err := s.pool.Query(ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var items []PortfolioItem
	for rows.Next() {
		var item PortfolioItem
		var iType string
		var tags []byte

		err := rows.Scan(
			&item.ID, &item.PortfolioID, &iType, &item.ItemID,
			&item.Title, &item.Status, &item.RiskLevel, &item.RiskScore, &item.Feasibility,
			&item.SortOrder, &tags, &item.Notes,
			&item.AddedAt, &item.AddedBy,
		)
		if err != nil {
			return nil, err
		}

		item.ItemType = ItemType(iType)
		json.Unmarshal(tags, &item.Tags)

		items = append(items, item)
	}

	return items, nil
}

// RemoveItem removes an item from a portfolio
func (s *Store) RemoveItem(ctx context.Context, id uuid.UUID) error {
	// Get portfolio ID first
	var portfolioID uuid.UUID
	err := s.pool.QueryRow(ctx,
		"SELECT portfolio_id FROM portfolio_items WHERE id = $1", id).Scan(&portfolioID)
	if err != nil {
		return err
	}

	_, err = s.pool.Exec(ctx, "DELETE FROM portfolio_items WHERE id = $1", id)
	if err != nil {
		return err
	}

	// Recalculate metrics
	return s.RecalculateMetrics(ctx, portfolioID)
}

// UpdateItemOrder updates the sort order of items
func (s *Store) UpdateItemOrder(ctx context.Context, portfolioID uuid.UUID, itemIDs []uuid.UUID) error {
	for i, id := range itemIDs {
		_, err := s.pool.Exec(ctx,
			"UPDATE portfolio_items SET sort_order = $2 WHERE id = $1 AND portfolio_id = $3",
			id, i+1, portfolioID)
		if err != nil {
			return err
		}
	}
	return nil
}

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

// MergePortfolios merges source portfolio into target
func (s *Store) MergePortfolios(ctx context.Context, req *MergeRequest, userID uuid.UUID) (*MergeResult, error) {
	result := &MergeResult{
		ConflictsResolved: []MergeConflict{},
	}

	// Get source items
	sourceItems, err := s.ListItems(ctx, req.SourcePortfolioID, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get source items: %w", err)
	}

	// Get target items for conflict detection
	targetItems, err := s.ListItems(ctx, req.TargetPortfolioID, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get target items: %w", err)
	}

	// Build map of existing items in target
	targetItemMap := make(map[uuid.UUID]bool)
	for _, item := range targetItems {
		targetItemMap[item.ItemID] = true
	}

	// Filter items based on strategy and options
	for _, item := range sourceItems {
		// Skip if not including this type
		if item.ItemType == ItemTypeRoadmap && !req.IncludeRoadmaps {
			result.ItemsSkipped++
			continue
		}
		if item.ItemType == ItemTypeWorkshop && !req.IncludeWorkshops {
			result.ItemsSkipped++
			continue
		}

		// Check for conflicts
		if targetItemMap[item.ItemID] {
			switch req.Strategy {
			case MergeStrategyUnion:
				// Skip duplicates
				result.ItemsSkipped++
				result.ConflictsResolved = append(result.ConflictsResolved, MergeConflict{
					ItemID:     item.ItemID,
					ItemType:   item.ItemType,
					Reason:     "duplicate",
					Resolution: "kept_target",
				})
			case MergeStrategyReplace:
				// Update existing item in target
				// For now, just skip (could implement update logic)
				result.ItemsUpdated++
				result.ConflictsResolved = append(result.ConflictsResolved, MergeConflict{
					ItemID:     item.ItemID,
					ItemType:   item.ItemType,
					Reason:     "duplicate",
					Resolution: "merged",
				})
			case MergeStrategyIntersect:
				// Keep only items that exist in both
				// Skip items not in target
				result.ItemsSkipped++
			}
			continue
		}

		// Add item to target
		newItem := &PortfolioItem{
			PortfolioID: req.TargetPortfolioID,
			ItemType:    item.ItemType,
			ItemID:      item.ItemID,
			Title:       item.Title,
			Status:      item.Status,
			RiskLevel:   item.RiskLevel,
			RiskScore:   item.RiskScore,
			Feasibility: item.Feasibility,
			Tags:        item.Tags,
			Notes:       item.Notes,
			AddedBy:     userID,
		}

		if err := s.AddItem(ctx, newItem); err != nil {
			// Skip on error but continue
			result.ItemsSkipped++
			continue
		}
		result.ItemsAdded++
	}

	// Delete source if requested
	if req.DeleteSource {
		if err := s.DeletePortfolio(ctx, req.SourcePortfolioID); err != nil {
			return nil, fmt.Errorf("failed to delete source portfolio: %w", err)
		}
		result.SourceDeleted = true
	}

	// Get updated target portfolio
	result.TargetPortfolio, _ = s.GetPortfolio(ctx, req.TargetPortfolioID)

	return result, nil
}

// ============================================================================
// Metrics and Statistics
// ============================================================================

// RecalculateMetrics recalculates aggregated metrics for a portfolio
func (s *Store) RecalculateMetrics(ctx context.Context, portfolioID uuid.UUID) error {
	// Count by type
	var totalAssessments, totalRoadmaps, totalWorkshops int
	s.pool.QueryRow(ctx,
		"SELECT COUNT(*) FROM portfolio_items WHERE portfolio_id = $1 AND item_type = 'ASSESSMENT'",
		portfolioID).Scan(&totalAssessments)
	s.pool.QueryRow(ctx,
		"SELECT COUNT(*) FROM portfolio_items WHERE portfolio_id = $1 AND item_type = 'ROADMAP'",
		portfolioID).Scan(&totalRoadmaps)
	s.pool.QueryRow(ctx,
		"SELECT COUNT(*) FROM portfolio_items WHERE portfolio_id = $1 AND item_type = 'WORKSHOP'",
		portfolioID).Scan(&totalWorkshops)

	// Calculate risk metrics from assessments
	var avgRiskScore float64
	var highRiskCount, conditionalCount, approvedCount int

	s.pool.QueryRow(ctx, `
		SELECT COALESCE(AVG(risk_score), 0)
		FROM portfolio_items
		WHERE portfolio_id = $1 AND item_type = 'ASSESSMENT'
	`, portfolioID).Scan(&avgRiskScore)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*)
		FROM portfolio_items
		WHERE portfolio_id = $1 AND item_type = 'ASSESSMENT' AND risk_level IN ('HIGH', 'UNACCEPTABLE')
	`, portfolioID).Scan(&highRiskCount)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*)
		FROM portfolio_items
		WHERE portfolio_id = $1 AND item_type = 'ASSESSMENT' AND feasibility = 'CONDITIONAL'
	`, portfolioID).Scan(&conditionalCount)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*)
		FROM portfolio_items
		WHERE portfolio_id = $1 AND item_type = 'ASSESSMENT' AND feasibility = 'YES'
	`, portfolioID).Scan(&approvedCount)

	// Calculate compliance score (simplified: % of approved items)
	var complianceScore float64
	if totalAssessments > 0 {
		complianceScore = (float64(approvedCount) / float64(totalAssessments)) * 100
	}

	// Update portfolio
	_, err := s.pool.Exec(ctx, `
		UPDATE portfolios SET
			total_assessments = $2,
			total_roadmaps = $3,
			total_workshops = $4,
			avg_risk_score = $5,
			high_risk_count = $6,
			conditional_count = $7,
			approved_count = $8,
			compliance_score = $9,
			updated_at = NOW()
		WHERE id = $1
	`,
		portfolioID,
		totalAssessments, totalRoadmaps, totalWorkshops,
		avgRiskScore, highRiskCount, conditionalCount, approvedCount,
		complianceScore,
	)

	return err
}

// GetPortfolioStats returns detailed statistics for a portfolio
func (s *Store) GetPortfolioStats(ctx context.Context, portfolioID uuid.UUID) (*PortfolioStats, error) {
	stats := &PortfolioStats{
		ItemsByType: make(map[ItemType]int),
	}

	// Total items
	s.pool.QueryRow(ctx,
		"SELECT COUNT(*) FROM portfolio_items WHERE portfolio_id = $1",
		portfolioID).Scan(&stats.TotalItems)

	// Items by type
	rows, _ := s.pool.Query(ctx, `
		SELECT item_type, COUNT(*)
		FROM portfolio_items
		WHERE portfolio_id = $1
		GROUP BY item_type
	`, portfolioID)
	if rows != nil {
		defer rows.Close()
		for rows.Next() {
			var itemType string
			var count int
			rows.Scan(&itemType, &count)
			stats.ItemsByType[ItemType(itemType)] = count
		}
	}

	// Risk distribution
	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM portfolio_items
		WHERE portfolio_id = $1 AND risk_level = 'MINIMAL'
	`, portfolioID).Scan(&stats.RiskDistribution.Minimal)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM portfolio_items
		WHERE portfolio_id = $1 AND risk_level = 'LOW'
	`, portfolioID).Scan(&stats.RiskDistribution.Low)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM portfolio_items
		WHERE portfolio_id = $1 AND risk_level = 'MEDIUM'
	`, portfolioID).Scan(&stats.RiskDistribution.Medium)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM portfolio_items
		WHERE portfolio_id = $1 AND risk_level = 'HIGH'
	`, portfolioID).Scan(&stats.RiskDistribution.High)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM portfolio_items
		WHERE portfolio_id = $1 AND risk_level = 'UNACCEPTABLE'
	`, portfolioID).Scan(&stats.RiskDistribution.Unacceptable)

	// Feasibility distribution
	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM portfolio_items
		WHERE portfolio_id = $1 AND feasibility = 'YES'
	`, portfolioID).Scan(&stats.FeasibilityDist.Yes)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM portfolio_items
		WHERE portfolio_id = $1 AND feasibility = 'CONDITIONAL'
	`, portfolioID).Scan(&stats.FeasibilityDist.Conditional)

	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM portfolio_items
		WHERE portfolio_id = $1 AND feasibility = 'NO'
	`, portfolioID).Scan(&stats.FeasibilityDist.No)

	// Average risk score
	s.pool.QueryRow(ctx, `
		SELECT COALESCE(AVG(risk_score), 0)
		FROM portfolio_items
		WHERE portfolio_id = $1 AND item_type = 'ASSESSMENT'
	`, portfolioID).Scan(&stats.AvgRiskScore)

	// Compliance score
	s.pool.QueryRow(ctx,
		"SELECT compliance_score FROM portfolios WHERE id = $1",
		portfolioID).Scan(&stats.ComplianceScore)

	// DSFA required count
	// This would need to join with ucca_assessments to get dsfa_recommended
	// For now, estimate from high risk items
	stats.DSFARequired = stats.RiskDistribution.High + stats.RiskDistribution.Unacceptable

	// Controls required (items with CONDITIONAL feasibility)
	stats.ControlsRequired = stats.FeasibilityDist.Conditional

	stats.LastUpdated = time.Now().UTC()

	return stats, nil
}

// GetPortfolioSummary returns a complete portfolio summary
func (s *Store) GetPortfolioSummary(ctx context.Context, portfolioID uuid.UUID) (*PortfolioSummary, error) {
	portfolio, err := s.GetPortfolio(ctx, portfolioID)
	if err != nil || portfolio == nil {
		return nil, err
	}

	items, err := s.ListItems(ctx, portfolioID, nil)
	if err != nil {
		return nil, err
	}

	stats, err := s.GetPortfolioStats(ctx, portfolioID)
	if err != nil {
		return nil, err
	}

	return &PortfolioSummary{
		Portfolio:        portfolio,
		Items:            items,
		RiskDistribution: stats.RiskDistribution,
		FeasibilityDist:  stats.FeasibilityDist,
	}, nil
}

// ============================================================================
// Bulk Operations
// ============================================================================

// BulkAddItems adds multiple items to a portfolio
func (s *Store) BulkAddItems(ctx context.Context, portfolioID uuid.UUID, items []PortfolioItem, userID uuid.UUID) (*BulkAddItemsResponse, error) {
	result := &BulkAddItemsResponse{
		Errors: []string{},
	}

	for _, item := range items {
		item.PortfolioID = portfolioID
		item.AddedBy = userID

		// Fetch item info from source table if not provided
		if item.Title == "" {
			s.populateItemInfo(ctx, &item)
		}

		if err := s.AddItem(ctx, &item); err != nil {
			result.Skipped++
			result.Errors = append(result.Errors, fmt.Sprintf("%s: %v", item.ItemID, err))
		} else {
			result.Added++
		}
	}

	return result, nil
}

// populateItemInfo fetches item metadata from the source table
func (s *Store) populateItemInfo(ctx context.Context, item *PortfolioItem) {
	switch item.ItemType {
	case ItemTypeAssessment:
		s.pool.QueryRow(ctx, `
			SELECT title, feasibility, risk_level, risk_score, status
			FROM ucca_assessments WHERE id = $1
		`, item.ItemID).Scan(&item.Title, &item.Feasibility, &item.RiskLevel, &item.RiskScore, &item.Status)

	case ItemTypeRoadmap:
		s.pool.QueryRow(ctx, `
			SELECT name, status
			FROM roadmaps WHERE id = $1
		`, item.ItemID).Scan(&item.Title, &item.Status)

	case ItemTypeWorkshop:
		s.pool.QueryRow(ctx, `
			SELECT title, status
			FROM workshop_sessions WHERE id = $1
		`, item.ItemID).Scan(&item.Title, &item.Status)
	}
}

// ============================================================================
// Activity Tracking
// ============================================================================

// LogActivity logs an activity entry for a portfolio
func (s *Store) LogActivity(ctx context.Context, portfolioID uuid.UUID, entry *ActivityEntry) error {
	_, err := s.pool.Exec(ctx, `
		INSERT INTO portfolio_activity (
			id, portfolio_id, timestamp, action,
			item_type, item_id, item_title, user_id
		) VALUES (
			$1, $2, $3, $4, $5, $6, $7, $8
		)
	`,
		uuid.New(), portfolioID, entry.Timestamp, entry.Action,
		string(entry.ItemType), entry.ItemID, entry.ItemTitle, entry.UserID,
	)
	return err
}

// GetRecentActivity retrieves recent activity for a portfolio
func (s *Store) GetRecentActivity(ctx context.Context, portfolioID uuid.UUID, limit int) ([]ActivityEntry, error) {
	if limit <= 0 {
		limit = 20
	}

	rows, err := s.pool.Query(ctx, `
		SELECT timestamp, action, item_type, item_id, item_title, user_id
		FROM portfolio_activity
		WHERE portfolio_id = $1
		ORDER BY timestamp DESC
		LIMIT $2
	`, portfolioID, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var activities []ActivityEntry
	for rows.Next() {
		var entry ActivityEntry
		var itemType string
		err := rows.Scan(
			&entry.Timestamp, &entry.Action, &itemType,
			&entry.ItemID, &entry.ItemTitle, &entry.UserID,
		)
		if err != nil {
			return nil, err
		}
		entry.ItemType = ItemType(itemType)
		activities = append(activities, entry)
	}

	return activities, nil
}
