package marketplace

import (
	"context"
	"errors"
	"fmt"

	"github.com/google/uuid"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"veza-backend-api/internal/models"
)

var (
	ErrProductNotFound   = errors.New("product not found")
	ErrOrderNotFound     = errors.New("order not found")
	ErrInsufficientFunds = errors.New("insufficient funds")
	ErrOrderFailed       = errors.New("order failed processing")
	ErrInvalidSeller     = errors.New("seller does not own the track")
	ErrTrackNotFound     = errors.New("track not found")
	ErrNoLicense         = errors.New("no valid license found")
)

// NewOrderItem represents an item to be ordered
type NewOrderItem struct {
	ProductID uuid.UUID
}

// StorageService defines the interface for file retrieval
type StorageService interface {
	// GetDownloadURL returns a signed URL or relative path for the file
	GetDownloadURL(ctx context.Context, filePath string) (string, error)
}

// MarketplaceService définit l'interface pour les opérations de la marketplace
type MarketplaceService interface {
	// Product Management
	CreateProduct(ctx context.Context, product *Product) error
	GetProduct(ctx context.Context, id uuid.UUID) (*Product, error)
	UpdateProduct(ctx context.Context, id uuid.UUID, sellerID uuid.UUID, updates map[string]interface{}) (*Product, error)
	ListProducts(ctx context.Context, filters map[string]interface{}) ([]Product, error)

	// Purchasing
	CreateOrder(ctx context.Context, buyerID uuid.UUID, items []NewOrderItem) (*Order, error)
	GetOrder(ctx context.Context, orderID uuid.UUID, buyerID uuid.UUID) (*Order, error)
	ListOrders(ctx context.Context, buyerID uuid.UUID) ([]Order, error)
	ProcessPaymentWebhook(ctx context.Context, payload []byte) error

	// Fulfillment
	GetDownloadURL(ctx context.Context, buyerID uuid.UUID, productID uuid.UUID) (string, error)
	GetUserLicenses(ctx context.Context, userID uuid.UUID) ([]License, error)
}

// Service implémente MarketplaceService
type Service struct {
	db      *gorm.DB
	logger  *zap.Logger
	storage StorageService
}

// NewService creates a new Marketplace service instance
func NewService(db *gorm.DB, logger *zap.Logger, storage StorageService) *Service {
	return &Service{
		db:      db,
		logger:  logger,
		storage: storage,
	}
}

// CreateProduct creates a new product listing
// Validates that the seller owns the track
func (s *Service) CreateProduct(ctx context.Context, product *Product) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		// 1. Validate Track existence and ownership if linked
		if product.ProductType == "track" && product.TrackID != nil {
			var track models.Track
			if err := tx.First(&track, "id = ?", product.TrackID).Error; err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					return ErrTrackNotFound
				}
				return err
			}

			// Verify ownership
			if track.UserID != product.SellerID {
				return ErrInvalidSeller
			}
		}

		// 2. Create Product
		if err := tx.Create(product).Error; err != nil {
			s.logger.Error("Failed to create product", zap.Error(err))
			return err
		}

		s.logger.Info("Product created successfully",
			zap.String("product_id", product.ID.String()),
			zap.String("seller_id", product.SellerID.String()))

		return nil
	})
}

// GetProduct retrieves a product by ID
func (s *Service) GetProduct(ctx context.Context, id uuid.UUID) (*Product, error) {
	var product Product
	if err := s.db.First(&product, "id = ?", id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrProductNotFound
		}
		return nil, err
	}
	return &product, nil
}

// UpdateProduct updates an existing product
// BE-API-037: Implement marketplace product update endpoint
// Validates that the seller owns the product
func (s *Service) UpdateProduct(ctx context.Context, id uuid.UUID, sellerID uuid.UUID, updates map[string]interface{}) (*Product, error) {
	var product Product
	if err := s.db.First(&product, "id = ?", id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrProductNotFound
		}
		return nil, err
	}

	// Verify ownership
	if product.SellerID != sellerID {
		return nil, ErrInvalidSeller
	}

	// Update allowed fields
	if title, ok := updates["title"].(string); ok && title != "" {
		product.Title = title
	}
	if description, ok := updates["description"].(string); ok {
		product.Description = description
	}
	if price, ok := updates["price"].(float64); ok && price > 0 {
		product.Price = price
	}
	if status, ok := updates["status"].(string); ok {
		product.Status = ProductStatus(status)
	}

	// Save updates
	if err := s.db.Save(&product).Error; err != nil {
		s.logger.Error("Failed to update product", zap.Error(err))
		return nil, err
	}

	s.logger.Info("Product updated successfully",
		zap.String("product_id", product.ID.String()),
		zap.String("seller_id", sellerID.String()))

	return &product, nil
}

// ListProducts retrieves products based on filters
func (s *Service) ListProducts(ctx context.Context, filters map[string]interface{}) ([]Product, error) {
	var products []Product
	query := s.db.Model(&Product{})

	// Filter by status (default to active)
	if status, ok := filters["status"]; ok {
		query = query.Where("status = ?", status)
	} else {
		query = query.Where("status = ?", ProductStatusActive)
	}

	// Filter by Seller ID
	if sellerID, ok := filters["seller_id"]; ok {
		query = query.Where("seller_id = ?", sellerID)
	}

	// Filter by Product Type
	if pType, ok := filters["product_type"]; ok && pType != "" {
		query = query.Where("product_type = ?", pType)
	}

	// Search by Title (Case Insensitive)
	if search, ok := filters["search"]; ok && search != "" {
		searchStr := fmt.Sprintf("%%%s%%", search)
		query = query.Where("title ILIKE ?", searchStr)
	}

	// Filter by Price Range
	if minPrice, ok := filters["min_price"]; ok {
		query = query.Where("price >= ?", minPrice)
	}
	if maxPrice, ok := filters["max_price"]; ok {
		query = query.Where("price <= ?", maxPrice)
	}

	// Pagination
	limit := 20
	if l, ok := filters["limit"].(int); ok && l > 0 {
		limit = l
	}
	offset := 0
	if o, ok := filters["offset"].(int); ok && o >= 0 {
		offset = o
	}

	// Order by creation date desc by default
	query = query.Order("created_at DESC")

	if err := query.Limit(limit).Offset(offset).Find(&products).Error; err != nil {
		return nil, err
	}
	return products, nil
}

// CreateOrder initiates a purchase transaction
// Transactional: Order -> Items -> Payment(Simulated) -> Licenses
func (s *Service) CreateOrder(ctx context.Context, buyerID uuid.UUID, items []NewOrderItem) (*Order, error) {
	var order *Order

	err := s.db.Transaction(func(tx *gorm.DB) error {
		totalAmount := 0.0
		var orderItems []OrderItem
		var productsToLicense []*Product

		// 1. Validate products and calculate total
		for _, item := range items {
			var product Product
			if err := tx.First(&product, "id = ?", item.ProductID).Error; err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					return fmt.Errorf("product %s not found", item.ProductID)
				}
				return err
			}

			if product.Status != ProductStatusActive {
				return fmt.Errorf("product %s is not active", item.ProductID)
			}

			totalAmount += product.Price
			orderItems = append(orderItems, OrderItem{
				ProductID: product.ID,
				Price:     product.Price,
			})
			productsToLicense = append(productsToLicense, &product)
		}

		// 2. Create Order (PENDING)
		order = &Order{
			BuyerID:     buyerID,
			TotalAmount: totalAmount,
			Currency:    "EUR", // Default for MVP
			Status:      "pending",
			Items:       orderItems,
		}

		if err := tx.Create(order).Error; err != nil {
			return err
		}

		// 3. Simulate Payment (Immediate Success for MVP)
		// In real scenario, we would pause here or interact with Stripe
		order.Status = "completed"
		order.PaymentIntent = "simulated_payment_" + uuid.New().String()
		if err := tx.Save(order).Error; err != nil {
			return err
		}

		// 4. Generate Licenses
		for _, prod := range productsToLicense {
			if prod.ProductType == "track" && prod.TrackID != nil {
				license := License{
					BuyerID:       buyerID,
					TrackID:       *prod.TrackID,
					ProductID:     prod.ID,
					OrderID:       order.ID,
					Type:          prod.LicenseType,
					Rights:        `{"streaming": true, "download": true}`, // Default rights
					DownloadsLeft: 3,                                       // Default limit
				}
				if err := tx.Create(&license).Error; err != nil {
					return err
				}
			}
		}

		return nil
	})

	if err != nil {
		s.logger.Error("Failed to create order", zap.Error(err), zap.String("buyer_id", buyerID.String()))
		return nil, err
	}

	s.logger.Info("Order created and processed successfully", zap.String("order_id", order.ID.String()))
	return order, nil
}

// GetOrder retrieves a specific order by ID
// BE-API-039: Implement marketplace order details endpoint
// Validates that the user owns the order
func (s *Service) GetOrder(ctx context.Context, orderID uuid.UUID, buyerID uuid.UUID) (*Order, error) {
	var order Order
	if err := s.db.WithContext(ctx).
		Where("id = ? AND buyer_id = ?", orderID, buyerID).
		Preload("Items").
		First(&order).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrOrderNotFound
		}
		s.logger.Error("Failed to get order", zap.Error(err))
		return nil, err
	}
	return &order, nil
}

// ListOrders retrieves all orders for a buyer
// BE-API-038: Implement marketplace order list endpoint
func (s *Service) ListOrders(ctx context.Context, buyerID uuid.UUID) ([]Order, error) {
	var orders []Order
	if err := s.db.WithContext(ctx).
		Where("buyer_id = ?", buyerID).
		Preload("Items").
		Order("created_at DESC").
		Find(&orders).Error; err != nil {
		s.logger.Error("Failed to list orders", zap.Error(err))
		return nil, err
	}
	return orders, nil
}

// ProcessPaymentWebhook handles payment confirmation
func (s *Service) ProcessPaymentWebhook(ctx context.Context, payload []byte) error {
	// MVP: Not implemented yet
	return nil
}

// GetDownloadURL checks license and returns signed URL for the asset
func (s *Service) GetDownloadURL(ctx context.Context, buyerID uuid.UUID, productID uuid.UUID) (string, error) {
	// 1. Check for valid license
	var license License
	err := s.db.Where("buyer_id = ? AND product_id = ? AND downloads_left > 0", buyerID, productID).
		First(&license).Error

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return "", ErrNoLicense
		}
		return "", err
	}

	// 2. Get Track info
	var track models.Track
	if err := s.db.First(&track, "id = ?", license.TrackID).Error; err != nil {
		return "", ErrTrackNotFound
	}

	// 3. Generate URL
	url, err := s.storage.GetDownloadURL(ctx, track.FilePath)
	if err != nil {
		return "", err
	}

	// 4. Decrement downloads left (Optional based on business rules)
	// In strict mode we might want to decrement here
	// s.db.Model(&license).Update("downloads_left", gorm.Expr("downloads_left - 1"))

	return url, nil
}

// GetUserLicenses returns all licenses owned by a user
func (s *Service) GetUserLicenses(ctx context.Context, userID uuid.UUID) ([]License, error) {
	var licenses []License
	if err := s.db.Where("buyer_id = ?", userID).Find(&licenses).Error; err != nil {
		return nil, err
	}
	return licenses, nil
}
