package chapa

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)

type Client struct {
	baseURL    string
	secretKey  string
	httpClient *http.Client
}

func NewClient(baseURL, secretKey string) *Client {
	return &Client{
		baseURL:   baseURL,
		secretKey: secretKey,
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

func (c *Client) InitializePayment(ctx context.Context, req domain.ChapaDepositRequest) (domain.ChapaDepositResponse, error) {
	payload := map[string]interface{}{
		"amount":       req.Amount,
		"currency":     req.Currency,
		"email":        req.Email,
		"first_name":   req.FirstName,
		"last_name":    req.LastName,
		"tx_ref":       req.TxRef,
		"callback_url": req.CallbackURL,
		"return_url":   req.ReturnURL,
	}

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

	httpReq, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/transaction/initialize", bytes.NewBuffer(payloadBytes))
	if err != nil {
		return domain.ChapaDepositResponse{}, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("Authorization", "Bearer "+c.secretKey)
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return domain.ChapaDepositResponse{}, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return domain.ChapaDepositResponse{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	var response struct {
		Message string `json:"message"`
		Status  string `json:"status"`
		Data    struct {
			CheckoutURL string `json:"checkout_url"`
		} `json:"data"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return domain.ChapaDepositResponse{}, fmt.Errorf("failed to decode response: %w", err)
	}

	return domain.ChapaDepositResponse{
		CheckoutURL: response.Data.CheckoutURL,
		// Reference:   req.TxRef,
	}, nil
}

func (c *Client) VerifyPayment(ctx context.Context, reference string) (domain.ChapaDepositVerification, error) {
	httpReq, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/transaction/verify/"+reference, nil)
	if err != nil {
		return domain.ChapaDepositVerification{}, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("Authorization", "Bearer "+c.secretKey)

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return domain.ChapaDepositVerification{}, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return domain.ChapaDepositVerification{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	var verification domain.ChapaDepositVerification

	if err := json.NewDecoder(resp.Body).Decode(&verification); err != nil {
		return domain.ChapaDepositVerification{}, fmt.Errorf("failed to decode response: %w", err)
	}

	var status domain.PaymentStatus
	switch verification.Status {
	case "success":
		status = domain.PaymentStatusCompleted
	default:
		status = domain.PaymentStatusFailed
	}

	return domain.ChapaDepositVerification{
		Status:   status,
		Amount:   verification.Amount,
		Currency: verification.Currency,
	}, nil
}

func (c *Client) ManualVerifyPayment(ctx context.Context, txRef string) (*domain.ChapaVerificationResponse, error) {
	url := fmt.Sprintf("%s/transaction/verify/%s", c.baseURL, txRef)

	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+c.secretKey)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	var response struct {
		Status   string  `json:"status"`
		Amount   float64 `json:"amount"`
		Currency string  `json:"currency"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	var status domain.PaymentStatus
	switch response.Status {
	case "success":
		status = domain.PaymentStatusCompleted
	default:
		status = domain.PaymentStatusFailed
	}

	return &domain.ChapaVerificationResponse{
		Status:   string(status),
		Amount:   response.Amount,
		Currency: response.Currency,
	}, nil
}

func (c *Client) FetchSupportedBanks(ctx context.Context) ([]domain.Bank, error) {
	req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/banks", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+c.secretKey)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	var bankResponse domain.BankResponse
	if err := json.NewDecoder(resp.Body).Decode(&bankResponse); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	var banks []domain.Bank
	for _, bankData := range bankResponse.Data {
		bank := domain.Bank{
			ID:            bankData.ID,
			Slug:          bankData.Slug,
			Swift:         bankData.Swift,
			Name:          bankData.Name,
			AcctLength:    bankData.AcctLength,
			CountryID:     bankData.CountryID,
			IsMobileMoney: bankData.IsMobileMoney,
			IsActive:      bankData.IsActive,
			IsRTGS:        bankData.IsRTGS,
			Active:        bankData.Active,
			Is24Hrs:       bankData.Is24Hrs,
			CreatedAt:     bankData.CreatedAt,
			UpdatedAt:     bankData.UpdatedAt,
			Currency:      bankData.Currency,
		}
		banks = append(banks, bank)
	}

	return banks, nil
}

func (c *Client) InitiateTransfer(ctx context.Context, req domain.ChapaWithdrawalRequest) (bool, error) {
	// base, err := url.Parse(c.baseURL)
	// if err != nil {
	// 	return false, fmt.Errorf("invalid base URL: %w", err)
	// }
	endpoint := c.baseURL + "/transfers"
	fmt.Printf("\n\nChapa withdrawal URL is %v\n\n", endpoint)

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

	httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(reqBody))
	if err != nil {
		return false, fmt.Errorf("failed to create request: %w", err)
	}

	c.setHeaders(httpReq)

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return false, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return false, fmt.Errorf("chapa api returned status: %d", resp.StatusCode)
	}

	var response domain.ChapaWithdrawalResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return false, fmt.Errorf("failed to decode response: %w", err)
	}

	return response.Status == string(domain.WithdrawalStatusProcessing), nil
}

func (c *Client) VerifyTransfer(ctx context.Context, reference string) (*domain.ChapaVerificationResponse, error) {
	base, err := url.Parse(c.baseURL)
	if err != nil {
		return nil, fmt.Errorf("invalid base URL: %w", err)
	}
	endpoint := base.ResolveReference(&url.URL{Path: fmt.Sprintf("/v1/transfers/%s/verify", reference)})

	httpReq, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	c.setHeaders(httpReq)

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("chapa api returned status: %d", resp.StatusCode)
	}

	var verification domain.ChapaVerificationResponse
	if err := json.NewDecoder(resp.Body).Decode(&verification); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &verification, nil
}

func (c *Client) setHeaders(req *http.Request) {
	req.Header.Set("Authorization", "Bearer "+c.secretKey)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
}
