package billing

import (
	"context"
	"time"

	"github.com/gpuaas/platform/packages/shared/events"
	"github.com/jackc/pgx/v5/pgxpool"
)

type Backend interface {
	GetBalance(context.Context, string) (int64, string, error)
	GetBalances(context.Context, string) ([]Balance, error)
	GetCurrencySettings(context.Context, string) (CurrencySettings, error)
	GetBillingAccount(context.Context, string) (BillingAccount, error)
	ListUsage(context.Context, string, Filters) ([]UsageRecord, *string, error)
	ExportUsageCSV(context.Context, string, Filters) (string, error)
	ListInvoices(context.Context, string, InvoiceFilters) ([]InvoiceSummary, *string, error)
	GetInvoice(context.Context, string, string) (*InvoiceDetail, error)
	ListBudgetPosture(context.Context, string, BudgetPostureFilters) ([]BudgetPostureItem, error)
	EvaluateBudgetDecision(context.Context, BudgetDecisionInput) (BudgetDecisionResult, error)
	ScanBudgetThresholds(context.Context, BudgetThresholdScanInput) (BudgetThresholdScanResult, error)
	ListRatedUsage(context.Context, string, RatedUsageFilters) ([]RatedUsageEvidenceLine, *string, error)
	GetRatedUsage(context.Context, string, string) (*RatedUsageEvidenceLine, error)
	DryRunShadowRating(context.Context, string, ShadowRatingInput) (ShadowRatingResult, error)
	SetFinancialRestriction(context.Context, FinancialRestrictionInput) (FinancialRestrictionResult, error)
	GetFinancialPosture(context.Context, string) (FinancialPostureResult, error)
	LinkProviderCustomer(context.Context, LinkProviderCustomerInput) (ProviderCustomer, error)
	RecordManagedIngressUsage(context.Context, ManagedIngressUsageInput) error
	RecordUsageMetered(context.Context, events.UsageMeteredPayload) error
	RateUsage(context.Context, RatingInput) (RatedUsageLine, error)
	ValidateUsageUnit(context.Context, string) error
	GenerateDraftInvoices(context.Context, GenerateDraftInvoicesInput) (GenerateDraftInvoicesResult, error)
}

type Service struct {
	backend Backend
}

func NewService(pool *pgxpool.Pool) *Service {
	return &Service{backend: newLegacyBackend(pool, nil)}
}

func NewServiceWithUsageUnitRegistry(pool *pgxpool.Pool, usageUnitRegistry UsageUnitRegistry) *Service {
	return &Service{backend: newLegacyBackend(pool, usageUnitRegistry)}
}

func NewServiceWithBackend(backend Backend) *Service {
	return &Service{backend: backend}
}

func (s *Service) GetBalance(ctx context.Context, userID string) (int64, string, error) {
	if s == nil || s.backend == nil {
		return 0, "", ErrInvalidRatingInput
	}
	amount, currency, err := s.backend.GetBalance(ctx, userID)
	return amount, currency, mapLegacyError(err)
}

func (s *Service) GetBalances(ctx context.Context, userID string) ([]Balance, error) {
	if s == nil || s.backend == nil {
		return nil, ErrInvalidRatingInput
	}
	items, err := s.backend.GetBalances(ctx, userID)
	return items, mapLegacyError(err)
}

func (s *Service) GetCurrencySettings(ctx context.Context, orgID string) (CurrencySettings, error) {
	if s == nil || s.backend == nil {
		return CurrencySettings{}, ErrInvalidRatingInput
	}
	item, err := s.backend.GetCurrencySettings(ctx, orgID)
	return item, mapLegacyError(err)
}

func (s *Service) GetBillingAccount(ctx context.Context, orgID string) (BillingAccount, error) {
	if s == nil || s.backend == nil {
		return BillingAccount{}, ErrInvalidRatingInput
	}
	item, err := s.backend.GetBillingAccount(ctx, orgID)
	return item, mapLegacyError(err)
}

func (s *Service) ListUsage(ctx context.Context, userID string, f Filters) ([]UsageRecord, *string, error) {
	if s == nil || s.backend == nil {
		return nil, nil, ErrInvalidRatingInput
	}
	items, next, err := s.backend.ListUsage(ctx, userID, f)
	return items, next, mapLegacyError(err)
}

func (s *Service) ExportUsageCSV(ctx context.Context, userID string, f Filters) (string, error) {
	if s == nil || s.backend == nil {
		return "", ErrInvalidRatingInput
	}
	value, err := s.backend.ExportUsageCSV(ctx, userID, f)
	return value, mapLegacyError(err)
}

func (s *Service) ListInvoices(ctx context.Context, orgID string, f InvoiceFilters) ([]InvoiceSummary, *string, error) {
	if s == nil || s.backend == nil {
		return nil, nil, ErrInvalidRatingInput
	}
	items, next, err := s.backend.ListInvoices(ctx, orgID, f)
	return items, next, mapLegacyError(err)
}

func (s *Service) GetInvoice(ctx context.Context, orgID, invoiceID string) (*InvoiceDetail, error) {
	if s == nil || s.backend == nil {
		return nil, ErrInvalidRatingInput
	}
	item, err := s.backend.GetInvoice(ctx, orgID, invoiceID)
	return item, mapLegacyError(err)
}

func (s *Service) ListBudgetPosture(ctx context.Context, orgID string, f BudgetPostureFilters) ([]BudgetPostureItem, error) {
	if s == nil || s.backend == nil {
		return nil, ErrInvalidBudgetInput
	}
	items, err := s.backend.ListBudgetPosture(ctx, orgID, f)
	return items, mapLegacyError(err)
}

func (s *Service) EvaluateBudgetDecision(ctx context.Context, in BudgetDecisionInput) (BudgetDecisionResult, error) {
	if s == nil || s.backend == nil {
		return BudgetDecisionResult{}, ErrInvalidBudgetInput
	}
	result, err := s.backend.EvaluateBudgetDecision(ctx, in)
	return result, mapLegacyError(err)
}

func (s *Service) ScanBudgetThresholds(ctx context.Context, in BudgetThresholdScanInput) (BudgetThresholdScanResult, error) {
	if s == nil || s.backend == nil {
		return BudgetThresholdScanResult{}, ErrInvalidBudgetInput
	}
	result, err := s.backend.ScanBudgetThresholds(ctx, in)
	return result, mapLegacyError(err)
}

func (s *Service) ListRatedUsage(ctx context.Context, orgID string, f RatedUsageFilters) ([]RatedUsageEvidenceLine, *string, error) {
	if s == nil || s.backend == nil {
		return nil, nil, ErrInvalidRatingInput
	}
	items, next, err := s.backend.ListRatedUsage(ctx, orgID, f)
	return items, next, mapLegacyError(err)
}

func (s *Service) GetRatedUsage(ctx context.Context, orgID, ratedUsageLineID string) (*RatedUsageEvidenceLine, error) {
	if s == nil || s.backend == nil {
		return nil, ErrInvalidRatingInput
	}
	item, err := s.backend.GetRatedUsage(ctx, orgID, ratedUsageLineID)
	return item, mapLegacyError(err)
}

func (s *Service) DryRunShadowRating(ctx context.Context, orgID string, in ShadowRatingInput) (ShadowRatingResult, error) {
	if s == nil || s.backend == nil {
		return ShadowRatingResult{}, ErrInvalidRatingInput
	}
	result, err := s.backend.DryRunShadowRating(ctx, orgID, in)
	return result, mapLegacyError(err)
}

func (s *Service) SetFinancialRestriction(ctx context.Context, in FinancialRestrictionInput) (FinancialRestrictionResult, error) {
	if s == nil || s.backend == nil {
		return FinancialRestrictionResult{}, ErrInvalidFinancialRestrictionInput
	}
	result, err := s.backend.SetFinancialRestriction(ctx, in)
	return result, mapLegacyError(err)
}

func (s *Service) GetFinancialPosture(ctx context.Context, userID string) (FinancialPostureResult, error) {
	if s == nil || s.backend == nil {
		return FinancialPostureResult{}, ErrInvalidFinancialRestrictionInput
	}
	result, err := s.backend.GetFinancialPosture(ctx, userID)
	return result, mapLegacyError(err)
}

func (s *Service) LinkProviderCustomer(ctx context.Context, in LinkProviderCustomerInput) (ProviderCustomer, error) {
	if s == nil || s.backend == nil {
		return ProviderCustomer{}, ErrInvalidProviderCustomerInput
	}
	result, err := s.backend.LinkProviderCustomer(ctx, in)
	return result, mapLegacyError(err)
}

func (s *Service) RecordManagedIngressUsage(ctx context.Context, in ManagedIngressUsageInput) error {
	if s == nil || s.backend == nil {
		return ErrInvalidRatingInput
	}
	return mapLegacyError(s.backend.RecordManagedIngressUsage(ctx, in))
}

func (s *Service) RecordUsageMetered(ctx context.Context, payload events.UsageMeteredPayload) error {
	if s == nil || s.backend == nil {
		return ErrInvalidRatingInput
	}
	return mapLegacyError(s.backend.RecordUsageMetered(ctx, payload))
}

func (s *Service) RateUsage(ctx context.Context, in RatingInput) (RatedUsageLine, error) {
	if s == nil || s.backend == nil {
		return RatedUsageLine{}, ErrInvalidRatingInput
	}
	result, err := s.backend.RateUsage(ctx, in)
	return result, mapLegacyError(err)
}

func (s *Service) ValidateUsageUnit(ctx context.Context, usageUnit string) error {
	if s == nil || s.backend == nil {
		return ErrInvalidRatingInput
	}
	return mapLegacyError(s.backend.ValidateUsageUnit(ctx, usageUnit))
}

func (s *Service) GenerateDraftInvoices(ctx context.Context, in GenerateDraftInvoicesInput) (GenerateDraftInvoicesResult, error) {
	if s == nil || s.backend == nil {
		return GenerateDraftInvoicesResult{}, ErrInvalidRatingInput
	}
	result, err := s.backend.GenerateDraftInvoices(ctx, in)
	return result, mapLegacyError(err)
}

func FinancialRestrictionEffects(state string) (map[string]any, error) {
	effects, err := legacyFinancialRestrictionEffects(state)
	return effects, mapLegacyError(err)
}

func LinkProviderCustomerWithStore(ctx context.Context, store ProviderCustomerStore, in LinkProviderCustomerInput) (ProviderCustomer, error) {
	item, err := legacyLinkProviderCustomerWithStore(ctx, store, in)
	return item, mapLegacyError(err)
}

func SetFinancialRestrictionWithStore(ctx context.Context, store FinancialRestrictionStore, in FinancialRestrictionInput) (FinancialRestrictionResult, error) {
	item, err := legacySetFinancialRestrictionWithStore(ctx, store, in)
	return item, mapLegacyError(err)
}

func InsertRatedUsageLine(ctx context.Context, store PostingStore, input RatedUsageLineInsertInput) (string, error) {
	id, err := legacyInsertRatedUsageLine(ctx, store, input)
	return id, mapLegacyError(err)
}

func PostRatedUsageLedgerEntry(ctx context.Context, store PostingStore, input LedgerPostingInput) (string, error) {
	id, err := legacyPostRatedUsageLedgerEntry(ctx, store, input)
	return id, mapLegacyError(err)
}

func PreviousMonthlyInvoicePeriod(now time.Time) (time.Time, time.Time) {
	return legacyPreviousMonthlyInvoicePeriod(now)
}

func RateUsage(input RatingInput) (RatedUsageLine, error) {
	line, err := legacyRateUsage(input)
	return line, mapLegacyError(err)
}
