package app

import (
	"context"
	"fmt"
	"time"

	"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
)

type TribeSnapshotRepository interface {
	// Create persists tribe snapshots in a store (e.g. Postgres).
	// Duplicates are ignored.
	Create(ctx context.Context, params ...domain.CreateTribeSnapshotParams) error
	ListWithRelations(
		ctx context.Context,
		params domain.ListTribeSnapshotsParams,
	) (domain.ListTribeSnapshotsWithRelationsResult, error)
	Delete(ctx context.Context, serverKey string, dateLTE time.Time) error
}

type TribeSnapshotService struct {
	repo     TribeSnapshotRepository
	tribeSvc *TribeService
	pub      SnapshotPublisher
}

func NewTribeSnapshotService(
	repo TribeSnapshotRepository,
	tribeSvc *TribeService,
	pub SnapshotPublisher,
) *TribeSnapshotService {
	return &TribeSnapshotService{repo: repo, tribeSvc: tribeSvc, pub: pub}
}

//nolint:gocyclo
func (svc *TribeSnapshotService) Create(
	ctx context.Context,
	createSnapshotsCmdPayload domain.CreateSnapshotsCmdPayload,
) error {
	serverKey := createSnapshotsCmdPayload.ServerKey()
	date := createSnapshotsCmdPayload.Date()

	listTribesParams := domain.NewListTribesParams()
	if err := listTribesParams.SetServerKeys([]string{serverKey}); err != nil {
		return fmt.Errorf("%s: %w", serverKey, err)
	}
	if err := listTribesParams.SetDeleted(domain.NullBool{
		V:     false,
		Valid: true,
	}); err != nil {
		return fmt.Errorf("%s: %w", serverKey, err)
	}
	if err := listTribesParams.SetSort([]domain.TribeSort{domain.TribeSortIDASC}); err != nil {
		return fmt.Errorf("%s: %w", serverKey, err)
	}
	if err := listTribesParams.SetLimit(domain.TribeListMaxLimit); err != nil {
		return fmt.Errorf("%s: %w", serverKey, err)
	}

	for {
		res, err := svc.tribeSvc.List(ctx, listTribesParams)
		if err != nil {
			return fmt.Errorf("%s: %w", serverKey, err)
		}
		tribes := res.Tribes()

		if len(tribes) == 0 {
			break
		}

		params, err := domain.NewCreateTribeSnapshotParams(tribes, date)
		if err != nil {
			return fmt.Errorf("%s: %w", serverKey, err)
		}

		if err = svc.repo.Create(ctx, params...); err != nil {
			return fmt.Errorf("%s: %w", serverKey, err)
		}

		if res.Next().IsZero() {
			break
		}

		if err = listTribesParams.SetCursor(res.Next()); err != nil {
			return fmt.Errorf("%s: %w", serverKey, err)
		}
	}

	payload, err := domain.NewSnapshotsCreatedEventPayload(serverKey, createSnapshotsCmdPayload.VersionCode())
	if err != nil {
		return fmt.Errorf("%s: couldn't construct domain.SnapshotsCreatedEventPayload: %w", serverKey, err)
	}

	if err = svc.pub.EventCreated(ctx, payload); err != nil {
		return fmt.Errorf("%s: %w", serverKey, err)
	}

	return nil
}

func (svc *TribeSnapshotService) ListWithRelations(
	ctx context.Context,
	params domain.ListTribeSnapshotsParams,
) (domain.ListTribeSnapshotsWithRelationsResult, error) {
	return svc.repo.ListWithRelations(ctx, params)
}

func (svc *TribeSnapshotService) CleanUp(ctx context.Context, payload domain.CleanUpDataCmdPayload) error {
	if !payload.CanCleanUpTribeSnapshots() {
		return nil
	}

	if err := svc.repo.Delete(
		ctx,
		payload.Server().Key(),
		time.Now().Add(-domain.TribeSnapshotRetentionForClosedServers),
	); err != nil {
		return fmt.Errorf("%s: %w", payload.Server().Key(), err)
	}

	return nil
}
