package services

import (
	"context"
	"fmt"
	"time"

	"github.com/jackc/pgx/v5/pgxpool"
	"ticketing-backend/internal/models"
)

const (
	DefaultOrderLockDuration = 10 * 60 // 10 minutes default
)

type OrderService struct {
	pool *pgxpool.Pool
}

func NewOrderService(pool *pgxpool.Pool) *OrderService {
	return &OrderService{pool: pool}
}

// CreateOrderLock creates a temporary lock on tickets with thread-safe capacity check
func (s *OrderService) CreateOrderLock(eventID, ticketClassID string, quantity int, userID string) (*models.OrderLock, error) {
	ctx := context.Background()

	// Get event settings for lock duration
	var lockDurationMinutes int
	err := s.pool.QueryRow(ctx,
		`SELECT COALESCE(queue_session_duration, 10) FROM events WHERE id = $1`, eventID,
	).Scan(&lockDurationMinutes)
	if err != nil {
		return nil, fmt.Errorf("event not found: %w", err)
	}

	lockDurationSeconds := lockDurationMinutes * 60

	// Use a transaction for thread-safe capacity check
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer tx.Rollback(ctx)

	// Lock the ticket class row for update
	var remainingCapacity int
	err = tx.QueryRow(ctx,
		`SELECT remaining_capacity FROM ticket_classes WHERE id = $1 AND event_id = $2 FOR UPDATE`,
		ticketClassID, eventID,
	).Scan(&remainingCapacity)
	if err != nil {
		return nil, fmt.Errorf("ticket class not found: %w", err)
	}

	// Calculate total locked quantity from active locks
	var totalLocked int
	err = tx.QueryRow(ctx,
		`SELECT COALESCE(SUM(quantity_locked), 0) FROM order_locks
		 WHERE event_id = $1 AND ticket_class_id = $2 AND status IN ('active', 'converted')`,
		eventID, ticketClassID,
	).Scan(&totalLocked)
	if err != nil {
		return nil, fmt.Errorf("failed to check locks: %w", err)
	}

	availableCapacity := remainingCapacity - totalLocked
	if availableCapacity < quantity {
		return nil, fmt.Errorf("insufficient ticket capacity: only %d available", availableCapacity)
	}

	// Create the lock
	expiresAt := time.Now().Add(time.Duration(lockDurationSeconds) * time.Second)
	var lock models.OrderLock
	err = tx.QueryRow(ctx,
		`INSERT INTO order_locks (event_id, ticket_class_id, quantity_locked, user_id, expires_at, status)
		 VALUES ($1, $2, $3, $4, $5, 'active')
		 RETURNING id, event_id, ticket_class_id, quantity_locked, user_id, expires_at`,
		eventID, ticketClassID, quantity, userID, expiresAt,
	).Scan(&lock.ID, &lock.EventID, &lock.TicketClassID, &lock.QuantityLocked, &lock.UserID, &lock.ExpiresAt)
	if err != nil {
		return nil, fmt.Errorf("failed to create lock: %w", err)
	}

	lock.Status = "active"

	if err := tx.Commit(ctx); err != nil {
		return nil, fmt.Errorf("failed to commit: %w", err)
	}

	return &lock, nil
}

// ReleaseExpiredLocks releases all expired locks for an event
func (s *OrderService) ReleaseExpiredLocks(eventID string) (int, error) {
	ctx := context.Background()
	result, err := s.pool.Exec(ctx,
		`UPDATE order_locks SET status = 'released'
		 WHERE event_id = $1 AND status = 'active' AND expires_at < NOW()`,
		eventID,
	)
	if err != nil {
		return 0, err
	}
	return int(result.RowsAffected()), nil
}

// ConvertLockToOrder converts a lock to a pending order
func (s *OrderService) ConvertLockToOrder(
	lockID, buyerName, buyerEmail, buyerPhone string,
	promoID, domainPromoID *string,
	discountAmount, originalAmount *float64,
) (*models.Order, error) {
	ctx := context.Background()

	// Get lock details
	var lock models.OrderLock
	err := s.pool.QueryRow(ctx,
		`SELECT event_id, ticket_class_id, quantity_locked, user_id FROM order_locks WHERE id = $1 AND status = 'active'`,
		lockID,
	).Scan(&lock.EventID, &lock.TicketClassID, &lock.QuantityLocked, &lock.UserID)
	if err != nil {
		return nil, fmt.Errorf("order lock not found or expired")
	}

	// Get ticket class price
	var price float64
	err = s.pool.QueryRow(ctx,
		`SELECT price FROM ticket_classes WHERE id = $1`, lock.TicketClassID,
	).Scan(&price)
	if err != nil {
		return nil, fmt.Errorf("ticket class not found")
	}

	baseAmount := price * float64(lock.QuantityLocked)
	finalOriginalAmount := baseAmount
	if originalAmount != nil {
		finalOriginalAmount = *originalAmount
	}
	finalDiscountAmount := float64(0)
	if discountAmount != nil {
		finalDiscountAmount = *discountAmount
	}
	totalAmount := finalOriginalAmount - finalDiscountAmount
	if totalAmount < 0 {
		totalAmount = 0
	}

	// Create order
	var order models.Order
	err = s.pool.QueryRow(ctx,
		`INSERT INTO orders (event_id, ticket_class_id, buyer_id, quantity, total_amount,
			buyer_name, buyer_email, buyer_phone, status, order_lock_id,
			promo_id, domain_promo_id, discount_amount, original_amount)
		 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
		 RETURNING id, event_id, ticket_class_id, buyer_id, quantity, total_amount, status, created_at`,
		lock.EventID, lock.TicketClassID, lock.UserID, lock.QuantityLocked,
		totalAmount, buyerName, buyerEmail, buyerPhone, "pending",
		lockID, promoID, domainPromoID, finalDiscountAmount, finalOriginalAmount,
	).Scan(&order.ID, &order.EventID, &order.TicketClassID, &order.BuyerID,
		&order.Quantity, &order.TotalAmount, &order.Status, &order.CreatedAt)

	if err != nil {
		return nil, fmt.Errorf("failed to create order: %w", err)
	}

	// Mark lock as converted
	_, _ = s.pool.Exec(ctx,
		`UPDATE order_locks SET status = 'converted' WHERE id = $1`, lockID,
	)

	return &order, nil
}

// CompleteOrder marks an order as completed and deducts capacity
func (s *OrderService) CompleteOrder(orderID string) error {
	ctx := context.Background()

	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return err
	}
	defer tx.Rollback(ctx)

	var ticketClassID string
	var quantity int
	err = tx.QueryRow(ctx,
		`SELECT ticket_class_id, quantity FROM orders WHERE id = $1 FOR UPDATE`,
		orderID,
	).Scan(&ticketClassID, &quantity)
	if err != nil {
		return fmt.Errorf("order not found")
	}

	_, err = tx.Exec(ctx,
		`UPDATE orders SET status = 'completed', updated_at = NOW() WHERE id = $1`,
		orderID,
	)
	if err != nil {
		return err
	}

	// Deduct remaining capacity atomically
	_, err = tx.Exec(ctx,
		`UPDATE ticket_classes SET remaining_capacity = remaining_capacity - $1 WHERE id = $2`,
		quantity, ticketClassID,
	)
	if err != nil {
		return err
	}

	return tx.Commit(ctx)
}

// CancelOrder cancels an order and releases locks
func (s *OrderService) CancelOrder(orderID string) error {
	ctx := context.Background()

	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return err
	}
	defer tx.Rollback(ctx)

	var status string
	var orderLockID *string
	var ticketClassID string
	var quantity int
	err = tx.QueryRow(ctx,
		`SELECT status, order_lock_id, ticket_class_id, quantity FROM orders WHERE id = $1 FOR UPDATE`,
		orderID,
	).Scan(&status, &orderLockID, &ticketClassID, &quantity)
	if err != nil {
		return fmt.Errorf("order not found")
	}

	// Update order status
	_, err = tx.Exec(ctx,
		`UPDATE orders SET status = 'cancelled', updated_at = NOW() WHERE id = $1`,
		orderID,
	)
	if err != nil {
		return err
	}

	// Release lock
	if orderLockID != nil {
		_, _ = tx.Exec(ctx,
			`UPDATE order_locks SET status = 'released' WHERE id = $1 AND status != 'released'`,
			*orderLockID,
		)
	}

	// Restore capacity only if order was completed
	if status == "completed" {
		_, err = tx.Exec(ctx,
			`UPDATE ticket_classes SET remaining_capacity = remaining_capacity + $1 WHERE id = $2`,
			quantity, ticketClassID,
		)
		if err != nil {
			return err
		}
	}

	return tx.Commit(ctx)
}
