package federation

import (
	"xorm.io/xorm"

	"github.com/forgeo/forgebucket/internal/models"
)

const activitiesPerPage = 20

// Collection builds an ActivityStreams OrderedCollection (or page) for an actor's outbox.
// page=0 returns the collection summary; page≥1 returns a paginated OrderedCollectionPage.
func Collection(db *xorm.Engine, actorAPID string, outboxURL string, page int) map[string]any {
	total, _ := db.Where("actor_ap_id = ? AND direction = 'outbound'", actorAPID).
		Count(&models.FederationActivity{})

	if page == 0 {
		return map[string]any{
			"@context":   "https://www.w3.org/ns/activitystreams",
			"id":         outboxURL,
			"type":       "OrderedCollection",
			"totalItems": total,
			"first":      outboxURL + "?page=1",
		}
	}

	offset := (page - 1) * activitiesPerPage
	var activities []models.FederationActivity
	db.Where("actor_ap_id = ? AND direction = 'outbound'", actorAPID).
		Desc("published").
		Limit(activitiesPerPage, offset).
		Find(&activities)

	items := make([]any, 0, len(activities))
	for _, a := range activities {
		items = append(items, rawJSON(a.ObjectJSON))
	}

	coll := map[string]any{
		"@context":     "https://www.w3.org/ns/activitystreams",
		"id":           outboxURL + "?page=" + itoa(page),
		"type":         "OrderedCollectionPage",
		"partOf":       outboxURL,
		"orderedItems": items,
	}
	if int64(offset+activitiesPerPage) < total {
		coll["next"] = outboxURL + "?page=" + itoa(page+1)
	}
	return coll
}

// StubCollection returns a minimal OrderedCollection with zero items.
// Used for followers/following until the social graph is implemented.
func StubCollection(collectionURL string) map[string]any {
	return map[string]any{
		"@context":   "https://www.w3.org/ns/activitystreams",
		"id":         collectionURL,
		"type":       "OrderedCollection",
		"totalItems": 0,
		"orderedItems": []any{},
	}
}

func itoa(n int) string {
	if n == 0 {
		return "0"
	}
	result := ""
	for n > 0 {
		result = string(rune('0'+n%10)) + result
		n /= 10
	}
	return result
}

// rawJSON wraps a JSON string so it marshals as-is (not double-encoded).
type rawJSON string

func (r rawJSON) MarshalJSON() ([]byte, error) {
	if r == "" {
		return []byte("null"), nil
	}
	return []byte(r), nil
}
