package handlers

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

// PaymentCredentialsServiceInterface defines the contract for credentials
type PaymentCredentialsServiceInterface interface {
	GetDecryptedKey(ctx context.Context, keyName string) (string, error)
}

// StripeClientInterface defines the contract for Stripe operations
type StripeClientInterface interface {
	CreateCheckoutSession(secretKey string, req CreateCheckoutRequest) (string, string, error)
}

// PaymentHandler handles Stripe payment operations
type PaymentHandler struct {
	credentialsService PaymentCredentialsServiceInterface
	stripeClient       StripeClientInterface
}

// NewPaymentHandler creates a new payment handler
func NewPaymentHandler(credentialsService PaymentCredentialsServiceInterface) *PaymentHandler {
	return &PaymentHandler{
		credentialsService: credentialsService,
		stripeClient:       &defaultStripeClient{},
	}
}

// defaultStripeClient implements StripeClientInterface
type defaultStripeClient struct{}

func (c *defaultStripeClient) CreateCheckoutSession(secretKey string, req CreateCheckoutRequest) (string, string, error) {
	return createStripeCheckoutSession(secretKey, req)
}

// CreateCheckoutRequest represents a checkout session request
type CreateCheckoutRequest struct {
	JobID      int    `json:"jobId"`
	PriceID    string `json:"priceId"`    // Stripe Price ID
	SuccessURL string `json:"successUrl"` // URL after success
	CancelURL  string `json:"cancelUrl"`  // URL after cancel
}

// CreateCheckoutResponse represents the checkout session response
type CreateCheckoutResponse struct {
	SessionID   string `json:"sessionId"`
	CheckoutURL string `json:"checkoutUrl"`
}

// StripeConfig holds the configuration for Stripe
type StripeConfig struct {
	SecretKey      string `json:"secretKey"`
	WebhookSecret  string `json:"webhookSecret"`
	PublishableKey string `json:"publishableKey"`
}

// CreateCheckout creates a Stripe checkout session for job posting payment
// @Summary Create checkout session
// @Description Create a Stripe checkout session for job posting payment
// @Tags Payments
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param request body CreateCheckoutRequest true "Checkout request"
// @Success 200 {object} CreateCheckoutResponse
// @Failure 400 {string} string "Bad Request"
// @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/payments/create-checkout [post]
func (h *PaymentHandler) CreateCheckout(w http.ResponseWriter, r *http.Request) {
	var req CreateCheckoutRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	if req.JobID == 0 || req.PriceID == "" {
		http.Error(w, "JobID and PriceID are required", http.StatusBadRequest)
		return
	}

	// Get Stripe config from encrypted vault
	var config StripeConfig
	payload, err := h.credentialsService.GetDecryptedKey(r.Context(), "stripe")
	if err == nil {
		json.Unmarshal([]byte(payload), &config)
	}

	// Fallback to Env if not found or empty (for migration/dev)
	if config.SecretKey == "" {
		config.SecretKey = os.Getenv("STRIPE_SECRET_KEY")
	}

	if config.SecretKey == "" {
		http.Error(w, "Payment service not configured", http.StatusInternalServerError)
		return
	}

	// Create Stripe checkout session via API
	sessionID, checkoutURL, err := h.stripeClient.CreateCheckoutSession(config.SecretKey, req)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to create checkout session: %v", err), http.StatusInternalServerError)
		return
	}

	response := CreateCheckoutResponse{
		SessionID:   sessionID,
		CheckoutURL: checkoutURL,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// HandleWebhook processes Stripe webhook events
// @Summary Handle Stripe webhook
// @Description Process Stripe webhook events (payment success, failure, etc.)
// @Tags Payments
// @Accept json
// @Produce json
// @Success 200 {string} string "OK"
// @Failure 400 {string} string "Bad Request"
// @Router /api/v1/payments/webhook [post]
func (h *PaymentHandler) HandleWebhook(w http.ResponseWriter, r *http.Request) {
	// Get Stripe config from encrypted vault
	var config StripeConfig
	payload, err := h.credentialsService.GetDecryptedKey(r.Context(), "stripe")
	if err == nil {
		json.Unmarshal([]byte(payload), &config)
	}

	// Fallback to Env
	if config.WebhookSecret == "" {
		config.WebhookSecret = os.Getenv("STRIPE_WEBHOOK_SECRET")
	}

	if config.WebhookSecret == "" {
		http.Error(w, "Webhook secret not configured", http.StatusInternalServerError)
		return
	}

	// Read body
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Failed to read body", http.StatusBadRequest)
		return
	}

	// Verify signature
	signature := r.Header.Get("Stripe-Signature")
	if !verifyStripeSignature(body, signature, config.WebhookSecret) {
		http.Error(w, "Invalid signature", http.StatusBadRequest)
		return
	}

	// Parse event
	var event map[string]interface{}
	if err := json.Unmarshal(body, &event); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}

	eventType, ok := event["type"].(string)
	if !ok {
		http.Error(w, "Missing event type", http.StatusBadRequest)
		return
	}

	// Handle event types
	switch eventType {
	case "checkout.session.completed":
		h.handleCheckoutComplete(event)
	case "payment_intent.succeeded":
		h.handlePaymentSuccess(event)
	case "payment_intent.payment_failed":
		h.handlePaymentFailed(event)
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte(`{"received": true}`))
}

func (h *PaymentHandler) handleCheckoutComplete(event map[string]interface{}) {
	// Extract session data and update job payment status
	data, _ := event["data"].(map[string]interface{})
	obj, _ := data["object"].(map[string]interface{})

	sessionID, _ := obj["id"].(string)
	metadata, _ := obj["metadata"].(map[string]interface{})
	jobIDStr, _ := metadata["job_id"].(string)

	if jobIDStr != "" && sessionID != "" {
		// TODO: Update job_payments table to mark as completed
		fmt.Printf("Payment completed for job %s, session %s\n", jobIDStr, sessionID)
	}
}

func (h *PaymentHandler) handlePaymentSuccess(_ map[string]interface{}) {
	// Payment succeeded
	fmt.Println("Payment succeeded")
}

func (h *PaymentHandler) handlePaymentFailed(_ map[string]interface{}) {
	// Payment failed
	fmt.Println("Payment failed")
}

// GetPaymentStatus returns the status of a payment
// @Summary Get payment status
// @Description Get the status of a job posting payment
// @Tags Payments
// @Produce json
// @Param id path string true "Payment ID"
// @Success 200 {object} map[string]interface{}
// @Failure 404 {string} string "Not Found"
// @Router /api/v1/payments/status/{id} [get]
func (h *PaymentHandler) GetPaymentStatus(w http.ResponseWriter, r *http.Request) {
	paymentID := r.PathValue("id")
	if paymentID == "" {
		http.Error(w, "Payment ID is required", http.StatusBadRequest)
		return
	}

	// TODO: Query job_payments table for status
	response := map[string]interface{}{
		"id":     paymentID,
		"status": "pending",
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// Helper function to create Stripe checkout session via API
func createStripeCheckoutSession(secretKey string, req CreateCheckoutRequest) (string, string, error) {
	client := &http.Client{}

	// Build form data
	data := fmt.Sprintf(
		"mode=payment&success_url=%s&cancel_url=%s&line_items[0][price]=%s&line_items[0][quantity]=1&metadata[job_id]=%d",
		req.SuccessURL, req.CancelURL, req.PriceID, req.JobID,
	)

	httpReq, err := http.NewRequest("POST", "https://api.stripe.com/v1/checkout/sessions",
		io.NopCloser(io.Reader(nil)))
	if err != nil {
		return "", "", err
	}

	httpReq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", secretKey))
	httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	httpReq.Body = io.NopCloser(strings.NewReader(data))

	resp, err := client.Do(httpReq)
	if err != nil {
		return "", "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		body, _ := io.ReadAll(resp.Body)
		return "", "", fmt.Errorf("Stripe API error: %s", string(body))
	}

	var result map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return "", "", err
	}

	sessionID, _ := result["id"].(string)
	checkoutURL, _ := result["url"].(string)

	return sessionID, checkoutURL, nil
}

// Verify Stripe webhook signature
func verifyStripeSignature(payload []byte, header, secret string) bool {
	if header == "" {
		return false
	}

	// Parse signature header
	var timestamp string
	var signatures []string

	parts := splitHeader(header)
	for _, p := range parts {
		if len(p) > 2 && p[0] == 't' && p[1] == '=' {
			timestamp = p[2:]
		} else if len(p) > 3 && p[0] == 'v' && p[1] == '1' && p[2] == '=' {
			signatures = append(signatures, p[3:])
		}
	}

	if timestamp == "" || len(signatures) == 0 {
		return false
	}

	// Check timestamp (5 min tolerance)
	ts, err := strconv.ParseInt(timestamp, 10, 64)
	if err != nil {
		return false
	}
	if time.Now().Unix()-ts > 300 {
		return false
	}

	// Compute expected signature
	signedPayload := fmt.Sprintf("%s.%s", timestamp, string(payload))
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(signedPayload))
	expectedSig := hex.EncodeToString(mac.Sum(nil))

	// Compare signatures
	for _, sig := range signatures {
		if hmac.Equal([]byte(sig), []byte(expectedSig)) {
			return true
		}
	}

	return false
}

func splitHeader(header string) []string {
	return strings.Split(header, ",")
}
