package services

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"time"

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

type TicketService struct {
	pool *pgxpool.Pool
}

func NewTicketService(pool *pgxpool.Pool) *TicketService {
	return &TicketService{pool: pool}
}

func (s *TicketService) GenerateBookingCode() string {
	timestamp := fmt.Sprintf("%X", time.Now().UnixMilli())
	random := make([]byte, 6)
	for i := range random {
		random[i] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[rand.Intn(36)]
	}
	return strings.ToUpper(timestamp) + string(random)
}

func (s *TicketService) GenerateQRCodeURL(bookingCode string) string {
	return fmt.Sprintf("https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=%s", bookingCode)
}

func (s *TicketService) ValidateBookingCode(code string) bool {
	return len(code) >= 13
}

func (s *TicketService) GenerateTickets(orderID string, buyerID *string, eventID, ticketClassID string, quantity int) ([]models.Ticket, error) {
	ctx := context.Background()

	var buyerName, buyerEmail, eventName, ticketClassName string
	var eventDate time.Time
	err := s.pool.QueryRow(ctx,
		`SELECT o.buyer_name, o.buyer_email, e.name, e.event_date, tc.name
		 FROM orders o
		 JOIN events e ON o.event_id = e.id
		 JOIN ticket_classes tc ON o.ticket_class_id = tc.id
		 WHERE o.id = $1`, orderID,
	).Scan(&buyerName, &buyerEmail, &eventName, &eventDate, &ticketClassName)
	if err != nil {
		return nil, fmt.Errorf("order not found: %w", err)
	}

	tickets := make([]models.Ticket, 0, quantity)
	for i := 0; i < quantity; i++ {
		bookingCode := s.GenerateBookingCode()
		qrCode := s.GenerateQRCodeURL(bookingCode)

		var ticket models.Ticket
		err := s.pool.QueryRow(ctx,
			`INSERT INTO tickets (order_id, booking_code, qr_code, ticket_class_name, buyer_name, buyer_email, event_name, event_date, status)
			 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'unused')
			 RETURNING id, booking_code, qr_code, ticket_class_name, buyer_name, buyer_email, event_name, event_date, status, created_at`,
			orderID, bookingCode, qrCode, ticketClassName, buyerName, buyerEmail, eventName, eventDate,
		).Scan(&ticket.ID, &ticket.BookingCode, &ticket.QRCode, &ticket.TicketClassName,
			&ticket.BuyerName, &ticket.BuyerEmail, &ticket.EventName, &ticket.EventDate,
			&ticket.Status, &ticket.CreatedAt)
		if err != nil {
			return nil, fmt.Errorf("failed to generate ticket: %w", err)
		}

		tickets = append(tickets, ticket)
	}

	return tickets, nil
}

func (s *TicketService) ValidateTicket(bookingCode string) (*models.Ticket, error) {
	ctx := context.Background()

	var ticket models.Ticket
	err := s.pool.QueryRow(ctx,
		`SELECT id, order_id, booking_code, qr_code, ticket_class_name, buyer_name, buyer_email,
		        event_name, event_date, status, used_at, used_by, created_at
		 FROM tickets WHERE booking_code = $1`, bookingCode,
	).Scan(&ticket.ID, &ticket.OrderID, &ticket.BookingCode, &ticket.QRCode,
		&ticket.TicketClassName, &ticket.BuyerName, &ticket.BuyerEmail,
		&ticket.EventName, &ticket.EventDate, &ticket.Status,
		&ticket.UsedAt, &ticket.UsedBy, &ticket.CreatedAt)

	if err != nil {
		return nil, fmt.Errorf("ticket not found")
	}

	return &ticket, nil
}

func (s *TicketService) UseTicket(ticketID, usedBy string) error {
	_, err := s.pool.Exec(context.Background(),
		`UPDATE tickets SET status = 'used', used_at = NOW(), used_by = $2
		 WHERE id = $1 AND status = 'unused'`,
		ticketID, usedBy,
	)
	return err
}
