package async

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

	"github.com/redis/go-redis/v9"
)

const (
	TaskEventQueueKey = "task_center:events:queue"
)

type EventType string

const (
	EventTypeOrderPaid    EventType = "order_paid"
	EventTypeInviteSuccess EventType = "invite_success"
)

type TaskEvent struct {
	Type      EventType `json:"type"`
	Payload   string    `json:"payload"`
	CreatedAt int64     `json:"created_at"`
}

type OrderPaidPayload struct {
	UserID  int64 `json:"user_id"`
	OrderID int64 `json:"order_id"`
}

type InviteSuccessPayload struct {
	InviterID int64 `json:"inviter_id"`
	InviteeID int64 `json:"invitee_id"`
}

type TaskQueue interface {
	PublishOrderPaid(ctx context.Context, userID, orderID int64) error
	PublishInviteSuccess(ctx context.Context, inviterID, inviteeID int64) error
	Consume(ctx context.Context) (*TaskEvent, error)
}

type redisTaskQueue struct {
	client *redis.Client
}

func NewRedisTaskQueue(client *redis.Client) TaskQueue {
	return &redisTaskQueue{client: client}
}

func (q *redisTaskQueue) PublishOrderPaid(ctx context.Context, userID, orderID int64) error {
	payload, _ := json.Marshal(OrderPaidPayload{UserID: userID, OrderID: orderID})
	event := TaskEvent{
		Type:      EventTypeOrderPaid,
		Payload:   string(payload),
		CreatedAt: time.Now().Unix(),
	}
	bytes, _ := json.Marshal(event)
	return q.client.LPush(ctx, TaskEventQueueKey, bytes).Err()
}

func (q *redisTaskQueue) PublishInviteSuccess(ctx context.Context, inviterID, inviteeID int64) error {
	payload, _ := json.Marshal(InviteSuccessPayload{InviterID: inviterID, InviteeID: inviteeID})
	event := TaskEvent{
		Type:      EventTypeInviteSuccess,
		Payload:   string(payload),
		CreatedAt: time.Now().Unix(),
	}
	bytes, _ := json.Marshal(event)
	return q.client.LPush(ctx, TaskEventQueueKey, bytes).Err()
}

func (q *redisTaskQueue) Consume(ctx context.Context) (*TaskEvent, error) {
	// Block for 2 seconds
	result, err := q.client.BRPop(ctx, 2*time.Second, TaskEventQueueKey).Result()
	if err != nil {
		if err == redis.Nil {
			return nil, nil // Timeout, no message
		}
		return nil, err
	}
	if len(result) < 2 {
		return nil, fmt.Errorf("invalid redis result")
	}
	
	var event TaskEvent
	if err := json.Unmarshal([]byte(result[1]), &event); err != nil {
		return nil, err
	}
	return &event, nil
}
