package payments

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/saveinmed/backend-go/internal/domain"
)

type MercadoPagoGateway struct {
	BaseURL               string
	AccessToken           string
	BackendURL            string
	MarketplaceCommission float64
}

func NewMercadoPagoGateway(baseURL, accessToken, backendURL string, commission float64) *MercadoPagoGateway {
	return &MercadoPagoGateway{
		BaseURL:               baseURL,
		AccessToken:           accessToken,
		BackendURL:            backendURL,
		MarketplaceCommission: commission,
	}
}

func (g *MercadoPagoGateway) CreatePreference(ctx context.Context, order *domain.Order, payer *domain.User, sellerAcc *domain.SellerPaymentAccount) (*domain.PaymentPreference, error) {
	// Construct items
	var items []map[string]interface{}
	for _, i := range order.Items {
		items = append(items, map[string]interface{}{
			"id":          i.ProductID.String(),
			"title":       "Produto", // Fallback
			"description": fmt.Sprintf("Product ID %s", i.ProductID),
			"quantity":    int(i.Quantity),
			"unit_price":  float64(i.UnitCents) / 100.0,
			"currency_id": "BRL",
		})
	}

	shipmentCost := float64(order.ShippingFeeCents) / 100.0
	notificationURL := g.BackendURL + "/api/v1/payments/webhook"

	payerData := map[string]interface{}{
		"email": payer.Email,
		"name":  payer.Name,
	}
	if payer.CPF != "" {
		payerData["identification"] = map[string]interface{}{
			"type":   "CPF",
			"number": payer.CPF,
		}
	}

	payload := map[string]interface{}{
		"items": items,
		"payer": payerData,
		"shipments": map[string]interface{}{
			"cost": shipmentCost,
			"mode": "not_specified",
		},
		"external_reference": order.ID.String(),
		"notification_url":   notificationURL,
		"binary_mode":        true,
		"back_urls": map[string]string{
			"success": g.BackendURL + "/checkout/success",
			"failure": g.BackendURL + "/checkout/failure",
			"pending": g.BackendURL + "/checkout/pending",
		},
		"auto_return": "approved",
	}

	// Calculate Fee
	svcFeeCents := int64(float64(order.TotalCents) * (g.MarketplaceCommission / 100))
	sellerRecCents := order.TotalCents - svcFeeCents

	// Add Split Payment logic (Transfers) if SellerAccount is provided
	if sellerAcc != nil && sellerAcc.AccountID != "" {
		// Try to parse AccountID (e.g. 123456789)
		// If AccountID contains non-digits, this might fail, MP User IDs are integers.
		// We'll trust it's a valid string representation of int.
		sellerMPID, err := strconv.ParseInt(sellerAcc.AccountID, 10, 64)
		if err == nil {
			// We transfer the seller's share to them.
			// Marketplace keeps the rest (which equals svcFee + Shipping if shipping is ours? Or Seller pays shipping?)
			// Usually, total = items + shipping.
			// If Seller pays commission on Total, then Seller gets (Total - Fee).
			// If Shipping is pass-through, we need to be careful.
			// Simple logic: Seller receives 'sellerRecCents'.

			payload["purpose"] = "wallet_purchase"
			payload["transfers"] = []map[string]interface{}{
				{
					"amount":       float64(sellerRecCents) / 100.0,
					"collector_id": sellerMPID,
					"description":  fmt.Sprintf("Venda SaveInMed #%s", order.ID.String()),
				},
			}
		} else {
			// Log error but proceed without split? Or fail?
			// Ideally we fail if we can't split.
			return nil, fmt.Errorf("invalid seller account id for split: %w", err)
		}
	}

	body, err := json.Marshal(payload)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, "POST", g.BaseURL+"/checkout/preferences", bytes.NewBuffer(body))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+g.AccessToken)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to call MP API: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("MP API failed with status %d", resp.StatusCode)
	}

	var result struct {
		ID        string `json:"id"`
		InitPoint string `json:"init_point"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &domain.PaymentPreference{
		OrderID:          order.ID,
		Gateway:          "mercadopago",
		PaymentID:        result.ID,
		PaymentURL:       result.InitPoint,
		CommissionPct:    g.MarketplaceCommission,
		MarketplaceFee:   svcFeeCents,
		SellerReceivable: sellerRecCents,
	}, nil
}

// CreatePayment executes a direct payment (Card/Pix) using a token (Bricks).
func (g *MercadoPagoGateway) CreatePayment(ctx context.Context, order *domain.Order, token, issuerID, paymentMethodID string, installments int, payer *domain.User, sellerAcc *domain.SellerPaymentAccount) (*domain.PaymentResult, error) {
	// shipmentCost folded into transaction_amount

	payerData := map[string]interface{}{
		"email":      payer.Email,
		"first_name": strings.Split(payer.Name, " ")[0],
	}
	// Handle Last Name if possible, or just send email. MP is lenient.
	if payer.CPF != "" {
		docType := "CPF"
		cleanDoc := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(payer.CPF, ".", ""), "-", ""), "/", "")
		if len(cleanDoc) > 11 {
			docType = "CNPJ"
		}
		payerData["identification"] = map[string]interface{}{
			"type":   docType,
			"number": cleanDoc,
		}
	}

	payload := map[string]interface{}{
		"transaction_amount": float64(order.TotalCents+order.ShippingFeeCents) / 100.0,
		"token":              token,
		"description":        fmt.Sprintf("Pedido #%s", order.ID.String()),
		"installments":       installments,
		"payment_method_id":  paymentMethodID,
		"issuer_id":          issuerID,
		"payer":              payerData,
		"external_reference": order.ID.String(),
		"notification_url":   g.BackendURL + "/api/v1/payments/webhook",
		"binary_mode":        true,
	}

	// [Remains unchanged...]
	// Determine Logic for Total Amount
	// If order.TotalCents is items, and ShippingFee is extra:
	// realTotal = TotalCents + ShippingFeeCents.
	// In CreatePreference, cost was separate. Here it's one blob.

	// Fee Calculation
	chargeAmountCents := order.TotalCents + order.ShippingFeeCents
	payload["transaction_amount"] = float64(chargeAmountCents) / 100.0

	// Split Payment Logic
	svcFeeCents := int64(float64(order.TotalCents) * (g.MarketplaceCommission / 100))
	sellerRecCents := chargeAmountCents - svcFeeCents

	if sellerAcc != nil && sellerAcc.AccountID != "" {
		sellerMPID, err := strconv.ParseInt(sellerAcc.AccountID, 10, 64)
		if err == nil {
			payload["application_fee"] = nil
			payload["transfers"] = []map[string]interface{}{
				{
					"amount":       float64(sellerRecCents) / 100.0,
					"collector_id": sellerMPID,
					"description":  fmt.Sprintf("Venda SaveInMed #%s", order.ID.String()),
				},
			}
		}
	}

	body, err := json.Marshal(payload)
	if err != nil {
		return nil, fmt.Errorf("marshal error: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, "POST", g.BaseURL+"/v1/payments", bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+g.AccessToken)
	req.Header.Set("X-Idempotency-Key", order.ID.String())

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("api error: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		buf := new(bytes.Buffer)
		buf.ReadFrom(resp.Body)
		bodyStr := buf.String()
		fmt.Printf("MP Error Body: %s\n", bodyStr) // Log to stdout
		return nil, fmt.Errorf("mp status %d: %s", resp.StatusCode, bodyStr)
	}

	var res struct {
		ID           int64  `json:"id"`
		Status       string `json:"status"`
		StatusDetail string `json:"status_detail"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
		return nil, err
	}

	return &domain.PaymentResult{
		PaymentID: fmt.Sprintf("%d", res.ID),
		Status:    res.Status,
		Gateway:   "mercadopago",
		Message:   res.StatusDetail,
	}, nil
}
