package jackbox

import (
	"crypto/hmac"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	"github.com/sirupsen/logrus"
)

// WebhookServer handles incoming webhooks from the Jackbox API
type WebhookServer struct {
	port            int
	secret          string
	messageCallback func(string) // Callback to broadcast messages
	log             *logrus.Entry
	server          *http.Server
}

// WebhookPayload represents the webhook event from the API
type WebhookPayload struct {
	Event     string `json:"event"`
	Timestamp string `json:"timestamp"`
	Data      struct {
		Session struct {
			ID          int  `json:"id"`
			IsActive    bool `json:"is_active"`
			GamesPlayed int  `json:"games_played"`
		} `json:"session"`
		Game struct {
			ID            int    `json:"id"`
			Title         string `json:"title"`
			PackName      string `json:"pack_name"`
			MinPlayers    int    `json:"min_players"`
			MaxPlayers    int    `json:"max_players"`
			ManuallyAdded bool   `json:"manually_added"`
		} `json:"game"`
	} `json:"data"`
}

// NewWebhookServer creates a new webhook server
func NewWebhookServer(port int, secret string, messageCallback func(string), log *logrus.Entry) *WebhookServer {
	return &WebhookServer{
		port:            port,
		secret:          secret,
		messageCallback: messageCallback,
		log:             log,
	}
}

// Start starts the webhook HTTP server
func (w *WebhookServer) Start() error {
	mux := http.NewServeMux()
	mux.HandleFunc("/webhook/jackbox", w.handleWebhook)
	
	// Health check endpoint
	mux.HandleFunc("/health", func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(http.StatusOK)
		rw.Write([]byte("OK"))
	})

	w.server = &http.Server{
		Addr:    fmt.Sprintf(":%d", w.port),
		Handler: mux,
	}

	w.log.Infof("Starting Jackbox webhook server on port %d", w.port)
	
	go func() {
		if err := w.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			w.log.Errorf("Webhook server error: %v", err)
		}
	}()

	return nil
}

// Stop stops the webhook server
func (w *WebhookServer) Stop() error {
	if w.server != nil {
		w.log.Info("Stopping Jackbox webhook server")
		return w.server.Close()
	}
	return nil
}

// handleWebhook processes incoming webhook requests
func (w *WebhookServer) handleWebhook(rw http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Read the raw body for signature verification
	body, err := io.ReadAll(r.Body)
	if err != nil {
		w.log.Errorf("Failed to read webhook body: %v", err)
		http.Error(rw, "Failed to read body", http.StatusBadRequest)
		return
	}
	defer r.Body.Close()

	// Verify signature
	signature := r.Header.Get("X-Webhook-Signature")
	if !w.verifySignature(signature, body) {
		w.log.Warn("Webhook signature verification failed")
		http.Error(rw, "Invalid signature", http.StatusUnauthorized)
		return
	}

	// Parse the webhook payload
	var payload WebhookPayload
	if err := json.Unmarshal(body, &payload); err != nil {
		w.log.Errorf("Failed to parse webhook payload: %v", err)
		http.Error(rw, "Invalid payload", http.StatusBadRequest)
		return
	}

	// Handle the event
	w.handleEvent(&payload)

	// Always respond with 200 OK
	rw.WriteHeader(http.StatusOK)
	rw.Write([]byte("OK"))
}

// verifySignature verifies the HMAC-SHA256 signature of the webhook
func (w *WebhookServer) verifySignature(signature string, body []byte) bool {
	if signature == "" || len(signature) < 7 || signature[:7] != "sha256=" {
		return false
	}

	// Extract the hex signature (after "sha256=")
	receivedSig := signature[7:]

	// Compute expected signature
	mac := hmac.New(sha256.New, []byte(w.secret))
	mac.Write(body)
	expectedSig := hex.EncodeToString(mac.Sum(nil))

	// Timing-safe comparison
	return subtle.ConstantTimeCompare([]byte(receivedSig), []byte(expectedSig)) == 1
}

// handleEvent processes webhook events
func (w *WebhookServer) handleEvent(payload *WebhookPayload) {
	switch payload.Event {
	case "game.added":
		w.handleGameAdded(payload)
	default:
		w.log.Debugf("Unhandled webhook event: %s", payload.Event)
	}
}

// handleGameAdded handles the game.added event
func (w *WebhookServer) handleGameAdded(payload *WebhookPayload) {
	game := payload.Data.Game
	
	w.log.Infof("Game added: %s from %s", game.Title, game.PackName)
	
	// Format the announcement message
	message := fmt.Sprintf("🎮 Coming up next: %s!", game.Title)
	
	// Broadcast to both chats via callback
	if w.messageCallback != nil {
		w.messageCallback(message)
	}
}

