package domain

import (
	"net/url"
	"strconv"
	"testing"
	"time"

	"source.toby3d.me/toby3d/hub/internal/common"
)

// Subscription is a unique relation to a topic by a subscriber that indicates
// it should receive updates for that topic.
type Subscription struct {
	// First creation datetime
	CreatedAt time.Time

	// Last updating datetime
	UpdatedAt time.Time

	// Datetime when subscription must be deleted
	ExpiredAt time.Time

	// Datetime synced with topic updating time
	SyncedAt time.Time

	Callback *url.URL
	Topic    *url.URL

	Secret Secret
}

func (s Subscription) AddQuery(q url.Values) {
	s.Secret.AddQuery(q)
	q.Add(common.HubTopic, s.Topic.String())
	q.Add(common.HubCallback, s.Callback.String())
	q.Add(common.HubLeaseSeconds, strconv.FormatFloat(s.LeaseSeconds(), 'g', 0, 64))
}

func (s Subscription) SUID() SUID {
	return SUID{
		topic:    s.Topic.String(),
		callback: s.Callback.String(),
	}
}

func (s Subscription) LeaseSeconds() float64 {
	return s.ExpiredAt.Sub(s.UpdatedAt).Round(time.Second).Seconds()
}

func (s Subscription) Synced(t Topic) bool {
	return s.SyncedAt.Equal(t.UpdatedAt) || s.SyncedAt.After(t.UpdatedAt)
}

func (s Subscription) Expired(ts time.Time) bool {
	return s.ExpiredAt.Before(ts)
}

func TestSubscription(tb testing.TB, callbackUrl string) *Subscription {
	tb.Helper()

	callback, err := url.Parse(callbackUrl)
	if err != nil {
		tb.Fatal(err)
	}

	ts := time.Now().UTC().Round(time.Second)
	secret := TestSecret(tb)

	return &Subscription{
		CreatedAt: ts,
		UpdatedAt: ts,
		ExpiredAt: ts.Add(10 * 24 * time.Hour).Round(time.Second),
		Callback:  callback,
		Topic:     &url.URL{Scheme: "https", Host: "example.com", Path: "/lipsum"},
		Secret:    *secret,
	}
}
