package app

import (
	"context"
	"errors"
	"fmt"
	"time"

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

type SnapshotService struct {
	versionSvc        *VersionService
	serverSvc         *ServerService
	tribeSnapshotPub  SnapshotPublisher
	playerSnapshotPub SnapshotPublisher
}

func NewSnapshotService(
	versionSvc *VersionService,
	serverSvc *ServerService,
	tribeSnapshotPublisher SnapshotPublisher,
	playerSnapshotPublisher SnapshotPublisher,
) *SnapshotService {
	return &SnapshotService{
		versionSvc:        versionSvc,
		serverSvc:         serverSvc,
		tribeSnapshotPub:  tribeSnapshotPublisher,
		playerSnapshotPub: playerSnapshotPublisher,
	}
}

func (svc *SnapshotService) Create(ctx context.Context) error {
	listVersionsRes, err := svc.versionSvc.List(ctx, domain.NewListVersionsParams())
	if err != nil {
		return err
	}
	versions := listVersionsRes.Versions()

	for _, v := range versions {
		loc, loopErr := time.LoadLocation(v.Timezone())
		if loopErr != nil {
			return fmt.Errorf("%s: %w", v.Code(), err)
		}

		year, month, day := time.Now().In(loc).Date()
		snapshotsCreatedAtLT := time.Date(year, month, day, 0, 0, 0, 0, loc)
		date := time.Date(year, month, day, 0, 0, 0, 0, time.UTC)

		if loopErr = errors.Join(
			svc.publishTribe(ctx, v, snapshotsCreatedAtLT, date),
			svc.publishPlayer(ctx, v, snapshotsCreatedAtLT, date),
		); loopErr != nil {
			return loopErr
		}
	}

	return nil
}

func (svc *SnapshotService) publishTribe(
	ctx context.Context,
	v domain.Version,
	snapshotsCreatedAtLT time.Time,
	date time.Time,
) error {
	params := domain.NewListServersParams()
	if err := params.SetVersionCodes([]string{v.Code()}); err != nil {
		return err
	}
	if err := params.SetOpen(domain.NullBool{
		V:     true,
		Valid: true,
	}); err != nil {
		return err
	}
	if err := params.SetTribeSnapshotsCreatedAtLT(domain.NullTime{
		V:     snapshotsCreatedAtLT,
		Valid: true,
	}); err != nil {
		return err
	}

	servers, err := svc.serverSvc.ListAll(ctx, params)
	if err != nil {
		return err
	}

	payloads, err := svc.toPayload(v, servers, date)
	if err != nil {
		return err
	}

	return svc.tribeSnapshotPub.CmdCreate(ctx, payloads...)
}

func (svc *SnapshotService) publishPlayer(
	ctx context.Context,
	v domain.Version,
	snapshotsCreatedAtLT time.Time,
	date time.Time,
) error {
	params := domain.NewListServersParams()
	if err := params.SetVersionCodes([]string{v.Code()}); err != nil {
		return err
	}
	if err := params.SetOpen(domain.NullBool{
		V:     true,
		Valid: true,
	}); err != nil {
		return err
	}
	if err := params.SetPlayerSnapshotsCreatedAtLT(domain.NullTime{
		V:     snapshotsCreatedAtLT,
		Valid: true,
	}); err != nil {
		return err
	}

	servers, err := svc.serverSvc.ListAll(ctx, params)
	if err != nil {
		return err
	}

	payloads, err := svc.toPayload(v, servers, date)
	if err != nil {
		return err
	}

	return svc.playerSnapshotPub.CmdCreate(ctx, payloads...)
}

func (svc *SnapshotService) toPayload(
	v domain.Version,
	servers domain.Servers,
	date time.Time,
) ([]domain.CreateSnapshotsCmdPayload, error) {
	payloads := make([]domain.CreateSnapshotsCmdPayload, 0, len(servers))

	for _, s := range servers {
		p, err := domain.NewCreateSnapshotsCmdPayload(s.Key(), v.Code(), v.Timezone(), date)
		if err != nil {
			return nil, fmt.Errorf("%s: couldn't construct domain.CreateSnapshotsCmdPayload: %w", s.Key(), err)
		}

		payloads = append(payloads, p)
	}

	return payloads, nil
}
