package portfolio

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

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

// ============================================================================
// 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:
				result.ItemsSkipped++
				result.ConflictsResolved = append(result.ConflictsResolved, MergeConflict{
					ItemID:     item.ItemID,
					ItemType:   item.ItemType,
					Reason:     "duplicate",
					Resolution: "kept_target",
				})
			case MergeStrategyReplace:
				result.ItemsUpdated++
				result.ConflictsResolved = append(result.ConflictsResolved, MergeConflict{
					ItemID:     item.ItemID,
					ItemType:   item.ItemType,
					Reason:     "duplicate",
					Resolution: "merged",
				})
			case MergeStrategyIntersect:
				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 {
			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
}
