package human

import (
	"context"
	"errors"
	"time"

	"github.com/flowy-live/llink/internal/utils"
	"github.com/jackc/pgx/v5/pgxpool"
)

//go:generate go tool mockgen -source ./service.go -destination ./mocks/service.go

var (
	ErrNotFound     = errors.New("human not found")
	ErrInvalidParam = errors.New("invalid param")
)

type Service interface {
	GetOrCreateByEmail(ctx context.Context, email string) (*Human, error)
	// GetByEmail returns ErrNotFound if no human found.
	GetByEmail(ctx context.Context, email string) (*Human, error)
	// GetByID returns ErrNotFound if no human found.
	GetByID(ctx context.Context, id string) (*Human, error)
	ListAll(ctx context.Context) ([]*Human, error)
	UpdateEmailNotificationsEnabled(ctx context.Context, id string, enabled bool) error
	UpdateLastEmailNotificationSentAt(ctx context.Context, id string, t time.Time) error
	UpdateAvatar(ctx context.Context, id string, objectID string) error
	DeleteAvatar(ctx context.Context, id string) error
}

type serviceImpl struct {
	repo repository
}

func NewService(pool *pgxpool.Pool) Service {
	return &serviceImpl{repo: newRepository(pool)}
}

func (s *serviceImpl) GetOrCreateByEmail(ctx context.Context, email string) (*Human, error) {
	email, err := utils.NormalizeEmail(email)
	if err != nil {
		return nil, err
	}

	h, err := s.repo.getByEmail(ctx, email)
	if err != nil {
		if errors.Is(err, errNotFound) {
			return s.repo.create(ctx, email)
		}
		return nil, err
	}
	return h, nil
}

func (s *serviceImpl) GetByEmail(ctx context.Context, email string) (*Human, error) {
	email, err := utils.NormalizeEmail(email)
	if err != nil {
		return nil, err
	}

	h, err := s.repo.getByEmail(ctx, email)
	if errors.Is(err, errNotFound) {
		return nil, ErrNotFound
	}
	return h, err
}

func (s *serviceImpl) GetByID(ctx context.Context, id string) (*Human, error) {
	h, err := s.repo.getByID(ctx, id)
	if errors.Is(err, errNotFound) {
		return nil, ErrNotFound
	}
	return h, err
}

func (s *serviceImpl) ListAll(ctx context.Context) ([]*Human, error) {
	return s.repo.listAll(ctx)
}

func (s *serviceImpl) UpdateEmailNotificationsEnabled(ctx context.Context, id string, enabled bool) error {
	err := s.repo.updateEmailNotificationsEnabled(ctx, id, enabled)
	if errors.Is(err, errNotFound) {
		return ErrNotFound
	}
	return err
}

func (s *serviceImpl) UpdateLastEmailNotificationSentAt(ctx context.Context, id string, t time.Time) error {
	err := s.repo.updateLastEmailNotificationSentAt(ctx, id, t)
	if errors.Is(err, errNotFound) {
		return ErrNotFound
	}
	return err
}

func (s *serviceImpl) UpdateAvatar(ctx context.Context, id string, objectID string) error {
	if objectID == "" {
		return ErrInvalidParam
	}
	err := s.repo.updateAvatarObjectID(ctx, id, utils.CreateOptionalString(objectID))
	if errors.Is(err, errNotFound) {
		return ErrNotFound
	}
	return err
}

func (s *serviceImpl) DeleteAvatar(ctx context.Context, id string) error {
	err := s.repo.updateAvatarObjectID(ctx, id, nil)
	if errors.Is(err, errNotFound) {
		return ErrNotFound
	}
	return err
}
