package subscriptions

import (
	"sort"
	"time"

	"code.secondbit.org/uuid.hg"
)

func stripeCustomerInMemstore(stripeCustomer string, m *Memstore) bool {
	for _, sub := range m.subscriptions {
		if sub.StripeCustomer == stripeCustomer {
			return true
		}
	}
	return false
}

func (m *Memstore) createSubscription(sub Subscription) error {
	m.subscriptionLock.Lock()
	defer m.subscriptionLock.Unlock()

	if _, ok := m.subscriptions[sub.ID.String()]; ok {
		return ErrSubscriptionAlreadyExists
	}
	if stripeCustomerInMemstore(sub.StripeCustomer, m) {
		return ErrStripeCustomerAlreadyExists
	}
	m.subscriptions[sub.ID.String()] = sub
	return nil
}

func (m *Memstore) updateSubscription(id uuid.ID, change SubscriptionChange) error {
	if change.IsEmpty() {
		return ErrSubscriptionChangeEmpty
	}

	m.subscriptionLock.Lock()
	defer m.subscriptionLock.Unlock()

	s, ok := m.subscriptions[id.String()]
	if !ok {
		return ErrSubscriptionNotFound
	}
	if change.StripeCustomer != nil {
		if stripeCustomerInMemstore(*change.StripeCustomer, m) {
			return ErrStripeCustomerAlreadyExists
		}
	}
	s.ApplyChange(change)
	m.subscriptions[id.String()] = s
	return nil
}

func (m *Memstore) deleteSubscription(id uuid.ID) error {
	m.subscriptionLock.Lock()
	defer m.subscriptionLock.Unlock()

	_, ok := m.subscriptions[id.String()]
	if !ok {
		return ErrSubscriptionNotFound
	}
	delete(m.subscriptions, id.String())
	return nil
}

func (m *Memstore) listSubscriptionsLastChargedBefore(cutoff time.Time) ([]Subscription, error) {
	m.subscriptionLock.RLock()
	defer m.subscriptionLock.RUnlock()

	var result []Subscription
	for _, s := range m.subscriptions {
		if cutoff.Before(s.LastCharged) {
			continue
		}
		result = append(result, s)
	}

	sorted := ByLastChargeDate(result)
	sort.Sort(sorted)
	result = []Subscription(sorted)

	return result, nil
}

func (m *Memstore) getSubscriptions(ids []uuid.ID) (map[string]Subscription, error) {
	if len(ids) < 1 {
		return map[string]Subscription{}, ErrNoSubscriptionID
	}
	m.subscriptionLock.RLock()
	defer m.subscriptionLock.RUnlock()

	result := map[string]Subscription{}

	for _, id := range ids {
		s, ok := m.subscriptions[id.String()]
		if !ok {
			continue
		}
		result[s.ID.String()] = s
	}
	return result, nil
}

func (m *Memstore) getSubscriptionByUser(id uuid.ID) (Subscription, error) {
	m.subscriptionLock.RLock()
	defer m.subscriptionLock.RUnlock()

	for _, subscription := range m.subscriptions {
		if subscription.UserID.Equal(id) {
			return subscription, nil
		}
	}

	return Subscription{}, ErrSubscriptionNotFound
}
