package sresponse

import (
	"net/http"
	"time"

	"github.com/tech/sendico/pkg/api/http/response"
	"github.com/tech/sendico/pkg/mlogger"
	"github.com/tech/sendico/pkg/model"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
	ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
)

type ledgerAccount struct {
	model.Describable `bson:",inline" json:",inline"`
	LedgerAccountRef  string            `json:"ledgerAccountRef"`
	OrganizationRef   string            `json:"organizationRef"`
	OwnerRef          string            `json:"ownerRef,omitempty"`
	AccountCode       string            `json:"accountCode"`
	AccountType       string            `json:"accountType"`
	Currency          string            `json:"currency"`
	Status            string            `json:"status"`
	AllowNegative     bool              `json:"allowNegative"`
	Role              string            `json:"role"`
	Metadata          map[string]string `json:"metadata,omitempty"`
	CreatedAt         time.Time         `json:"createdAt,omitempty"`
	UpdatedAt         time.Time         `json:"updatedAt,omitempty"`
}

type ledgerAccountsResponse struct {
	authResponse `json:",inline"`
	Accounts     []ledgerAccount `json:"accounts"`
}

type ledgerAccountResponse struct {
	authResponse `json:",inline"`
	Account      ledgerAccount `json:"account"`
}

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

type ledgerBalance struct {
	LedgerAccountRef string       `json:"ledgerAccountRef"`
	Balance          *ledgerMoney `json:"balance,omitempty"`
	Version          int64        `json:"version"`
	LastUpdated      time.Time    `json:"lastUpdated,omitempty"`
}

type ledgerBalanceResponse struct {
	authResponse `json:",inline"`
	Balance      ledgerBalance `json:"balance"`
}

func LedgerAccounts(logger mlogger.Logger, accounts []*ledgerv1.LedgerAccount, accessToken *TokenData) http.HandlerFunc {
	dto := make([]ledgerAccount, 0, len(accounts))
	for _, acc := range accounts {
		dto = append(dto, toLedgerAccount(acc))
	}
	return response.Ok(logger, ledgerAccountsResponse{
		Accounts:     dto,
		authResponse: authResponse{AccessToken: *accessToken},
	})
}

func LedgerAccountCreated(logger mlogger.Logger, account *ledgerv1.LedgerAccount, accessToken *TokenData) http.HandlerFunc {
	return response.Created(logger, ledgerAccountResponse{
		Account:      toLedgerAccount(account),
		authResponse: authResponse{AccessToken: *accessToken},
	})
}

func LedgerBalance(logger mlogger.Logger, resp *ledgerv1.BalanceResponse, accessToken *TokenData) http.HandlerFunc {
	return response.Ok(logger, ledgerBalanceResponse{
		Balance:      toLedgerBalance(resp),
		authResponse: authResponse{AccessToken: *accessToken},
	})
}

func toLedgerAccount(acc *ledgerv1.LedgerAccount) ledgerAccount {
	if acc == nil {
		return ledgerAccount{}
	}
	return ledgerAccount{
		Describable: model.Describable{
			Name:        acc.GetDescribable().GetName(),
			Description: acc.GetDescribable().Description,
		},
		LedgerAccountRef: acc.GetLedgerAccountRef(),
		OrganizationRef:  acc.GetOrganizationRef(),
		OwnerRef:         acc.GetOwnerRef(),
		AccountCode:      acc.GetAccountCode(),
		AccountType:      acc.GetAccountType().String(),
		Currency:         acc.GetCurrency(),
		Status:           acc.GetStatus().String(),
		AllowNegative:    acc.GetAllowNegative(),
		Role:             acc.GetRole().String(),
		Metadata:         acc.GetMetadata(),
		CreatedAt:        acc.GetCreatedAt().AsTime(),
		UpdatedAt:        acc.GetUpdatedAt().AsTime(),
	}
}

func toLedgerBalance(resp *ledgerv1.BalanceResponse) ledgerBalance {
	if resp == nil {
		return ledgerBalance{}
	}
	return ledgerBalance{
		LedgerAccountRef: resp.GetLedgerAccountRef(),
		Balance:          toLedgerMoney(resp.GetBalance()),
		Version:          resp.GetVersion(),
		LastUpdated:      resp.GetLastUpdated().AsTime(),
	}
}

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