package service

import (
	"context"
	"time"

	"github.com/charmingruby/homunc/apps/fraud/internal/fraud/delivery/event/publisher"
	"github.com/charmingruby/homunc/apps/fraud/internal/fraud/model"
	"github.com/charmingruby/homunc/apps/fraud/internal/fraud/repository"
	"github.com/charmingruby/homunc/apps/fraud/internal/shared/core"
	"github.com/charmingruby/homunc/apps/fraud/pkg/telemetry"
)

const (
	MaxBlockCheckAttempts = 3
	NightAmountThreshold  = 5000
	NightBlockStartHour   = 22
	NightBlockEndHour     = 6
)

type CheckTransferAvailabilityInput struct {
	SessionID   string
	UserID      string
	Country     string
	Amount      int
	TriggeredAt time.Time
}

func (s *Service) CheckTransferAvailability(ctx context.Context, in CheckTransferAvailabilityInput) error {
	now := time.Now()

	existentBlockedSource, err := s.blockedSourceRepo.FindByCheckoutDetails(ctx, repository.CheckoutDetails{
		UserID:    in.UserID,
		SessionID: in.SessionID,
		Country:   in.Country,
	})

	if err != nil {
		return err
	}

	alreadyWarned := existentBlockedSource.ID != ""
	hasBlock := existentBlockedSource.BlockedUntil != nil

	if alreadyWarned && hasBlock && existentBlockedSource.BlockedUntil.Before(now) {
		existentBlockedSource.Attempts = 0
		existentBlockedSource.BlockedUntil = nil
		existentBlockedSource.WarnedAt = nil
		existentBlockedSource.UpdatedAt = &now

		if err := s.blockedSourceRepo.Update(ctx, existentBlockedSource); err != nil {
			return err
		}
	}

	if !s.violatesNightAmountRule(in.Amount, in.TriggeredAt) {
		message := publisher.DispatchCheckoutAvailabilityStatusMessage{
			UserID:    in.UserID,
			SessionID: in.SessionID,
			Country:   in.Country,
		}

		telemetry.Log.Debug(
			"dispatched transfer result",
			"status", publisher.SecuredStatus,
			"message", message,
		)

		return s.pub.DispatchCheckoutAvailabilityStatus(
			context.TODO(),
			publisher.SecuredStatus,
			message,
		)
	}

	if !alreadyWarned {
		newBlockedSource := model.BlockedSource{
			ID:           core.NewID(),
			SessionID:    in.SessionID,
			UserID:       in.UserID,
			Country:      in.Country,
			Attempts:     1,
			CreatedAt:    now,
			UpdatedAt:    &now,
			WarnedAt:     &now,
			BlockedUntil: nil,
		}

		if err := s.blockedSourceRepo.Create(ctx, newBlockedSource); err != nil {
			return err
		}

		message := publisher.DispatchCheckoutAvailabilityStatusMessage{
			UserID:    in.UserID,
			SessionID: in.SessionID,
			Country:   in.Country,
		}

		telemetry.Log.Debug(
			"dispatched transfer result",
			"status", publisher.WarnedStatus,
			"message", message,
		)

		return s.pub.DispatchCheckoutAvailabilityStatus(
			context.TODO(),
			publisher.WarnedStatus,
			message,
		)
	}

	currentAttempt := existentBlockedSource.Attempts + 1

	if currentAttempt == MaxBlockCheckAttempts {
		existentBlockedSource.Attempts = currentAttempt

		blockForOneDay := now.Add(time.Hour * 24)
		existentBlockedSource.BlockedUntil = &blockForOneDay

		existentBlockedSource.WarnedAt = &now
		existentBlockedSource.UpdatedAt = &now

		if err := s.blockedSourceRepo.Update(ctx, existentBlockedSource); err != nil {
			return err
		}

		message := publisher.DispatchCheckoutAvailabilityStatusMessage{
			UserID:    in.UserID,
			SessionID: in.SessionID,
			Country:   in.Country,
		}

		telemetry.Log.Debug(
			"dispatched transfer result",
			"status", publisher.WarnedStatus,
			"message", message,
		)

		return s.pub.DispatchCheckoutAvailabilityStatus(
			context.TODO(),
			publisher.BlockedStatus,
			message,
		)
	}

	existentBlockedSource.Attempts = currentAttempt
	existentBlockedSource.WarnedAt = &now
	existentBlockedSource.UpdatedAt = &now

	if err := s.blockedSourceRepo.Update(ctx, existentBlockedSource); err != nil {
		return err
	}

	message := publisher.DispatchCheckoutAvailabilityStatusMessage{
		UserID:    in.UserID,
		SessionID: in.SessionID,
		Country:   in.Country,
	}

	telemetry.Log.Debug(
		"dispatched transfer result",
		"status", publisher.WarnedStatus,
		"message", message,
	)

	return s.pub.DispatchCheckoutAvailabilityStatus(
		context.TODO(),
		publisher.WarnedStatus,
		message,
	)
}

func (s *Service) violatesNightAmountRule(amount int, triggeredAt time.Time) bool {
	exceedsAmount := amount > NightAmountThreshold
	hour := triggeredAt.Hour()
	isInRestrictedHour := hour >= NightBlockStartHour || hour < NightBlockEndHour

	return exceedsAmount && isInRestrictedHour
}
