package services

import (
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/google/uuid"
	"github.com/midtrans/midtrans-go"
	"github.com/midtrans/midtrans-go/snap"

	"lune/talentscale/internal/modules/billing/dto"
)

// MidtransService handles all Midtrans SNAP API interactions
type MidtransService struct {
	serverKey    string
	clientKey    string
	isProduction bool
	snapClient   snap.Client
}

// MidtransSnapResponse holds the response from Midtrans SNAP API
type MidtransSnapResponse struct {
	SnapToken   string `json:"snap_token"`
	RedirectURL string `json:"redirect_url"`
}

// NewMidtransService creates a new Midtrans service instance
func NewMidtransService() *MidtransService {
	serverKey := os.Getenv("MIDTRANS_SERVER_KEY")
	clientKey := os.Getenv("MIDTRANS_CLIENT_KEY")
	isProd := os.Getenv("MIDTRANS_IS_PRODUCTION") == "true"

	var env midtrans.EnvironmentType
	if isProd {
		env = midtrans.Production
	} else {
		env = midtrans.Sandbox
	}

	var s snap.Client
	s.New(serverKey, env)

	log.Printf("✅ Midtrans initialized (production=%v)", isProd)

	return &MidtransService{
		serverKey:    serverKey,
		clientKey:    clientKey,
		isProduction: isProd,
		snapClient:   s,
	}
}

// CreateSnapTransaction creates a new Midtrans SNAP transaction
func (m *MidtransService) CreateSnapTransaction(
	orderID string,
	amount int64,
	companyName string,
	companyEmail string,
	packageName string,
	durationMonth int,
) (*MidtransSnapResponse, error) {

	itemDetail := midtrans.ItemDetails{
		ID:    packageName,
		Name:  fmt.Sprintf("%s - %d Month(s)", packageName, durationMonth),
		Price: amount,
		Qty:   1,
	}

	req := &snap.Request{
		TransactionDetails: midtrans.TransactionDetails{
			OrderID:  orderID,
			GrossAmt: amount,
		},
		CustomerDetail: &midtrans.CustomerDetails{
			FName: companyName,
			Email: companyEmail,
		},
		Items: &[]midtrans.ItemDetails{itemDetail},
		EnabledPayments: snap.AllSnapPaymentType,
		Expiry: &snap.ExpiryDetails{
			Unit:     "hour",
			Duration: 24,
		},
	}

	snapResp, err := m.snapClient.CreateTransaction(req)
	if err != nil {
		return nil, fmt.Errorf("midtrans create transaction failed: %w", err)
	}

	return &MidtransSnapResponse{
		SnapToken:   snapResp.Token,
		RedirectURL: snapResp.RedirectURL,
	}, nil
}

// VerifyWebhookSignature validates the Midtrans webhook signature
// Signature = SHA512(order_id + status_code + gross_amount + server_key)
func (m *MidtransService) VerifyWebhookSignature(notification *dto.MidtransNotification) bool {
	raw := notification.OrderID + notification.StatusCode + notification.GrossAmount + m.serverKey
	hash := sha512.Sum512([]byte(raw))
	computed := hex.EncodeToString(hash[:])

	return strings.EqualFold(computed, notification.SignatureKey)
}

// MapPaymentStatus maps Midtrans transaction_status to our internal status
func (m *MidtransService) MapPaymentStatus(transactionStatus string, fraudStatus string) string {
	switch transactionStatus {
	case "capture":
		if fraudStatus == "accept" {
			return "PAID"
		}
		return "PENDING"
	case "settlement":
		return "PAID"
	case "pending":
		return "PENDING"
	case "deny", "cancel", "expire", "failure":
		return "FAILED"
	default:
		return "PENDING"
	}
}

// IsSuccessStatus returns true if the mapped status indicates successful payment
func (m *MidtransService) IsSuccessStatus(status string) bool {
	return status == "PAID"
}

// IsFailedStatus returns true if the mapped status indicates failed/expired payment
func (m *MidtransService) IsFailedStatus(status string) bool {
	return status == "FAILED"
}

// ExtractPaymentMethod extracts the payment type from the notification
func (m *MidtransService) ExtractPaymentMethod(notification *dto.MidtransNotification) string {
	if notification.PaymentType != "" {
		return notification.PaymentType
	}
	return "unknown"
}

// GenerateOrderID creates a unique order ID for Midtrans
func GenerateOrderID(invoiceID uuid.UUID) string {
	return fmt.Sprintf("INV-%s-%d", invoiceID.String(), generateShortUID())
}

// generateShortUID returns a short numeric suffix for order ID uniqueness
func generateShortUID() int64 {
	uid := uuid.New()
	// Use first 8 bytes as int64
	var n int64
	for i := 0; i < 8; i++ {
		n = (n << 8) | int64(uid[i])
	}
	if n < 0 {
		n = -n
	}
	return n % 100000 // 5-digit suffix
}

// GetClientKey returns the client key for frontend use
func (m *MidtransService) GetClientKey() string {
	return m.clientKey
}
