package sresponse

import (
	"net/http"
	"time"

	"github.com/tech/sendico/pkg/api/http/response"
	"github.com/tech/sendico/pkg/mlogger"
	gatewayv1 "github.com/tech/sendico/pkg/proto/chain/gateway/v1"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
	paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
	"google.golang.org/protobuf/types/known/timestamppb"
)

type walletAsset struct {
	Chain           string `json:"chain"`
	TokenSymbol     string `json:"tokenSymbol"`
	ContractAddress string `json:"contractAddress"`
}

type wallet struct {
	WalletRef       string            `json:"walletRef"`
	OrganizationRef string            `json:"organizationRef"`
	OwnerRef        string            `json:"ownerRef"`
	Asset           walletAsset       `json:"asset"`
	DepositAddress  string            `json:"depositAddress"`
	Status          string            `json:"status"`
	Metadata        map[string]string `json:"metadata,omitempty"`
	CreatedAt       string            `json:"createdAt,omitempty"`
	UpdatedAt       string            `json:"updatedAt,omitempty"`
}

type walletsResponse struct {
	authResponse `json:",inline"`
	Wallets      []wallet                         `json:"wallets"`
	Page         *paginationv1.CursorPageResponse `json:"page,omitempty"`
}

type walletMoney struct {
	Amount   string `json:"amount"`
	Currency string `json:"currency"`
}

type walletBalance struct {
	Available       *walletMoney `json:"available,omitempty"`
	PendingInbound  *walletMoney `json:"pendingInbound,omitempty"`
	PendingOutbound *walletMoney `json:"pendingOutbound,omitempty"`
	CalculatedAt    string       `json:"calculatedAt,omitempty"`
}

type walletBalanceResponse struct {
	authResponse `json:",inline"`
	Balance      walletBalance `json:"balance"`
}

func Wallets(logger mlogger.Logger, resp *gatewayv1.ListManagedWalletsResponse, accessToken *TokenData) http.HandlerFunc {
	dto := walletsResponse{
		Page:         resp.GetPage(),
		authResponse: authResponse{AccessToken: *accessToken},
	}
	dto.Wallets = make([]wallet, 0, len(resp.GetWallets()))
	for _, w := range resp.GetWallets() {
		dto.Wallets = append(dto.Wallets, toWallet(w))
	}
	return response.Ok(logger, dto)
}

func WalletBalance(logger mlogger.Logger, bal *gatewayv1.WalletBalance, accessToken *TokenData) http.HandlerFunc {
	return response.Ok(logger, walletBalanceResponse{
		Balance:      toWalletBalance(bal),
		authResponse: authResponse{AccessToken: *accessToken},
	})
}

func toWallet(w *gatewayv1.ManagedWallet) wallet {
	if w == nil {
		return wallet{}
	}
	asset := w.GetAsset()
	chain := ""
	token := ""
	contract := ""
	if asset != nil {
		chain = asset.GetChain().String()
		token = asset.GetTokenSymbol()
		contract = asset.GetContractAddress()
	}
	return wallet{
		WalletRef:       w.GetWalletRef(),
		OrganizationRef: w.GetOrganizationRef(),
		OwnerRef:        w.GetOwnerRef(),
		Asset: walletAsset{
			Chain:           chain,
			TokenSymbol:     token,
			ContractAddress: contract,
		},
		DepositAddress: w.GetDepositAddress(),
		Status:         w.GetStatus().String(),
		Metadata:       w.GetMetadata(),
		CreatedAt:      tsToString(w.GetCreatedAt()),
		UpdatedAt:      tsToString(w.GetUpdatedAt()),
	}
}

func toWalletBalance(b *gatewayv1.WalletBalance) walletBalance {
	if b == nil {
		return walletBalance{}
	}
	return walletBalance{
		Available:       toMoney(b.GetAvailable()),
		PendingInbound:  toMoney(b.GetPendingInbound()),
		PendingOutbound: toMoney(b.GetPendingOutbound()),
		CalculatedAt:    tsToString(b.GetCalculatedAt()),
	}
}

func toMoney(m *moneyv1.Money) *walletMoney {
	if m == nil {
		return nil
	}
	return &walletMoney{
		Amount:   m.GetAmount(),
		Currency: m.GetCurrency(),
	}
}

func tsToString(ts *timestamppb.Timestamp) string {
	if ts == nil {
		return ""
	}
	return ts.AsTime().UTC().Format(time.RFC3339)
}
