package skeleton

import (
	"bytes"
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"time"

	detaildoEntity "refund/internal/entity/user/detaildo"
	ecommerceEntity "refund/internal/entity/user/ecommerce"
	paymentmethodEntity "refund/internal/entity/user/paymentmethod"
	pelapakEntity "refund/internal/entity/user/pelapak"
	refundEntity "refund/internal/entity/user/refund"
	shopeeEntity "refund/internal/entity/user/shopee"
	"refund/pkg/errors"
)

// UserData ...
type UserData interface {
	GetListPelapak(ctx context.Context, orderidecommerce string) ([]pelapakEntity.Pelapak, error)
	GetEcommerceId(ctx context.Context, OrderIDEcommerce string, rh_orderIdEcommerce string) ([]ecommerceEntity.Ecommerce, []refundEntity.RefundH, error)
	GetDetailDo(ctx context.Context, TransfH_NoTransf string) (map[string][]detaildoEntity.DetailDo, error)
	InsertRefundH(ctx context.Context, user refundEntity.RefundH) error
	InsertRefundD(ctx context.Context, user refundEntity.RefundD) error
	UpdateRefundD(ctx context.Context, OrderID string, refundd refundEntity.RefundD) error
	GetRefundD(ctx context.Context, ID string) (map[string][]refundEntity.RefundD, error)
	GetPaymentMethod(ctx context.Context, ID string) ([]paymentmethodEntity.PaymentMethod, error)
	InsertPaymentMethod(ctx context.Context, pay paymentmethodEntity.PaymentMethod) error
	GetMaxPaymentID(ctx context.Context) (int64, error)
}

// Service ...
type Service struct {
	userData UserData
}

// New ...
func New(userData UserData) Service {
	return Service{
		userData: userData,
	}
}

// GetListPelapak ...
func (s Service) GetListPelapak(ctx context.Context, orderidecommerce string) ([]pelapakEntity.Pelapak, error) {

	idecommerce := fmt.Sprintf("%%%s%%", orderidecommerce)
	pelapak, err := s.userData.GetListPelapak(ctx, idecommerce)
	if err != nil {
		return pelapak, errors.Wrap(err, "[SERVICE][GetListPelapak]")
	}

	return pelapak, err
}

// GetDetailDo ...
func (s Service) GetDetailDo(ctx context.Context, TransfH_NoTransf string) (map[string][]detaildoEntity.DetailDo, error) {
	detaildo, err := s.userData.GetDetailDo(ctx, TransfH_NoTransf)
	if err != nil {
		return detaildo, errors.Wrap(err, "[SERVICE][GetListPelapak]")
	}

	return detaildo, err
}

//GetEcommerceId ...
func (s Service) GetEcommerceId(ctx context.Context, OrderIDEcommerce string, rh_orderIdEcommerce string) (map[string]interface{}, error) {
	idecommerce := fmt.Sprintf("%%%s%%", OrderIDEcommerce)
	refundh := fmt.Sprintf("%%%s%%", rh_orderIdEcommerce)
	EcomID, RefundH, err := s.userData.GetEcommerceId(ctx, idecommerce, refundh)
	shopeeResult, err := s.GetShopeeResult(OrderIDEcommerce)
	var mapResult map[string]interface{}
	mapResult = make(map[string]interface{})
	mapResult["ecomData"] = EcomID
	mapResult["refundData"] = RefundH
	mapResult["shopeeData"] = shopeeResult
	if err != nil {
		return mapResult, errors.Wrap(err, "[SERVICE][GetLEcommerceID]")
	}

	return mapResult, err
}

// InsertRefundH ...
func (s Service) InsertRefundH(ctx context.Context, user refundEntity.RefundH) error {
	err := s.userData.InsertRefundH(ctx, user)
	if err != nil {
		return errors.Wrap(err, "[SERVICE][InsertRefundH]")
	}
	return err
}

// InsertRefundD ...
func (s Service) InsertRefundD(ctx context.Context, user []refundEntity.RefundD) error {
	var err error

	log.Println(user)
	log.Println(len(user))
	for i := 0; i < len(user); i++ {
		err = s.userData.InsertRefundD(ctx, user[i])
		log.Println(user[i])
	}
	if err != nil {
		return errors.Wrap(err, "[SERVICE][InsertRefundD]")
	}
	return err
}

//UpdateRefundD ...
func (s Service) UpdateRefundD(ctx context.Context, OrderID string, refundd refundEntity.RefundD) error {
	err := s.userData.UpdateRefundD(ctx, OrderID, refundd)
	if err != nil {
		return errors.Wrap(err, "[SERVICE][UpdateRefundD]")
	}
	return err
}

// GetShopeePaymentMethod ...
func (s Service) GetShopeePaymentMethod(Ordersn string) (string, error) {

	var apiUrl string = "https://partner.shopeemobile.com/api/v1/orders/detail"
	var apiKey string = "f4786d228ff53ae6090f3f735b32402a12d11988c8d8fdcc75aca0a5522ef750"

	var ordersnList []string
	ordersnList = append(ordersnList, Ordersn)

	body1 := map[string]interface{}{
		"ordersn_list": ordersnList,
		"partner_id":   844098,
		"shopid":       175375997,
		"timestamp":    time.Now().Unix(),
	}

	var jsonBody1 []byte
	jsonBody1, err := json.Marshal(body1)
	if err != nil {
		log.Println(err)
	}

	var signature string = apiUrl + "|" + string(jsonBody1)

	h := hmac.New(sha256.New, []byte(apiKey))
	h.Write([]byte(signature))

	sha := hex.EncodeToString(h.Sum(nil))

	req, err := http.NewRequest(http.MethodPost, apiUrl, bytes.NewBuffer(jsonBody1))
	if err != nil {
		log.Println("Error Request Post")
	}
	req.Header.Add("Authorization", sha)
	req.Header.Add("Content-Type", "application/json")

	response, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Println("Error Response Post")
	}

	defer response.Body.Close()

	body2, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Println("Error Read All Response")
	}

	var jsonObj map[string][]interface{}
	json.Unmarshal([]byte(body2), &jsonObj)

	var paymentMethod1 string
	for _, result := range jsonObj["orders"] {
		paymentMethod2 := result.(map[string]interface{})["payment_method"]
		paymentMethod1 = fmt.Sprintf("%v", paymentMethod2)
	}

	var shopee string
	shopee = paymentMethod1
	log.Println(shopee)

	return shopee, err
}

// GetShopeeResult ...
func (s Service) GetShopeeResult(Ordersn string) (shopeeEntity.ShopeeResult, error) {

	var apiUrl string = "https://partner.shopeemobile.com/api/v1/orders/my_income"
	var apiKey string = "f4786d228ff53ae6090f3f735b32402a12d11988c8d8fdcc75aca0a5522ef750"

	var ordersnList []string
	ordersnList = append(ordersnList, Ordersn)

	body1 := map[string]interface{}{
		"ordersn":    Ordersn,
		"partner_id": 844098,
		"shopid":     175375997,
		"timestamp":  time.Now().Unix(),
	}

	var jsonBody1 []byte
	jsonBody1, err := json.Marshal(body1)
	if err != nil {
		log.Println(err)
	}

	var signature string = apiUrl + "|" + string(jsonBody1)

	h := hmac.New(sha256.New, []byte(apiKey))
	h.Write([]byte(signature))

	sha := hex.EncodeToString(h.Sum(nil))

	req, err := http.NewRequest(http.MethodPost, apiUrl, bytes.NewBuffer(jsonBody1))
	if err != nil {
		log.Println("Error Request Post")
	}
	req.Header.Add("Authorization", sha)
	req.Header.Add("Content-Type", "application/json")

	response, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Println("Error Response Post")
	}

	defer response.Body.Close()

	body2, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Println("Error Read All Response")
	}

	var jsonObj map[string]interface{}
	json.Unmarshal([]byte(body2), &jsonObj)

	result := jsonObj["order"].(map[string]interface{})
	result2 := result["income_details"].(map[string]interface{})
	payment, err := s.GetShopeePaymentMethod(Ordersn)

	var shopeeResult shopeeEntity.ShopeeResult
	shopeeResult.Voucher, err = strconv.Atoi(fmt.Sprintf("%v", result2["voucher"]))
	shopeeResult.Coin, err = strconv.Atoi(fmt.Sprintf("%v", result2["coin"]))
	shopeeResult.PaymentMethod = payment
	log.Println(shopeeResult.Voucher)
	log.Println(shopeeResult.Coin)
	log.Println(shopeeResult.PaymentMethod)

	return shopeeResult, err
}

// GetRefundD ...
func (s Service) GetRefundD(ctx context.Context, ID string) (map[string][]refundEntity.RefundD, error) {
	refund, err := s.userData.GetRefundD(ctx, ID)
	if err != nil {
		return refund, errors.Wrap(err, "[SERVICE][GetRefundD]")
	}

	return refund, err
}

// GetPaymentMethod ...
func (s Service) GetPaymentMethod(ctx context.Context, ID string) ([]paymentmethodEntity.PaymentMethod, error) {

	payment, err := s.userData.GetPaymentMethod(ctx, ID)
	if err != nil {
		return payment, errors.Wrap(err, "[SERVICE][GetPaymentMethod]")
	}

	return payment, err
}

// InsertPaymentMethod ...
func (s Service) InsertPaymentMethod(ctx context.Context, pay paymentmethodEntity.PaymentMethod) error {
	var (
		err error
	)

	temp, err := s.userData.GetMaxPaymentID(ctx)
	pay.PaymentMethodID = temp
	err = s.userData.InsertPaymentMethod(ctx, pay)
	if err != nil {
		return errors.Wrap(err, "[SERVICE][InsertPaymentMethod]")
	}
	return err
}
