package service

import (
	"context"
	"fmt"
	"sync/atomic"

	"github.com/genex/translate-service/internal/domain/entity"
	"github.com/genex/translate-service/internal/domain/repository"
)

// TranslateService is the application service that orchestrates address mapping
// operations. It depends on the domain repository interface, not a concrete
// implementation — following the Dependency Inversion Principle.
type TranslateService struct {
	repo      repository.AddressMappingRepository
	idCounter atomic.Int64
}

// NewTranslateService creates a new TranslateService with the injected repository.
func NewTranslateService(repo repository.AddressMappingRepository) *TranslateService {
	return &TranslateService{
		repo: repo,
	}
}

// CreateMapping validates inputs via the domain entity factory, persists the
// mapping through the repository, and returns the created entity.
func (s *TranslateService) CreateMapping(ctx context.Context, userID, internalAddr, chainAddr, chainType string) (*entity.AddressMapping, error) {
	id := fmt.Sprintf("map-%d", s.idCounter.Add(1))

	mapping, err := entity.NewAddressMapping(id, userID, internalAddr, chainAddr, chainType)
	if err != nil {
		return nil, fmt.Errorf("domain validation failed: %w", err)
	}

	if err := s.repo.Save(ctx, mapping); err != nil {
		return nil, fmt.Errorf("failed to save mapping: %w", err)
	}

	return mapping, nil
}

// InternalToChain resolves an internal platform address to the corresponding
// on-chain mapping.
func (s *TranslateService) InternalToChain(ctx context.Context, internalAddr string) (*entity.AddressMapping, error) {
	mapping, err := s.repo.FindByInternalAddress(ctx, internalAddr)
	if err != nil {
		return nil, fmt.Errorf("repository lookup failed: %w", err)
	}
	return mapping, nil
}

// ChainToInternal resolves an on-chain address (with chain type) to the
// corresponding internal platform mapping.
func (s *TranslateService) ChainToInternal(ctx context.Context, chain, chainAddr string) (*entity.AddressMapping, error) {
	mapping, err := s.repo.FindByChainAddress(ctx, chain, chainAddr)
	if err != nil {
		return nil, fmt.Errorf("repository lookup failed: %w", err)
	}
	return mapping, nil
}

// GetByUserID returns all address mappings belonging to the specified user.
func (s *TranslateService) GetByUserID(ctx context.Context, userID string) ([]*entity.AddressMapping, error) {
	mappings, err := s.repo.FindByUserID(ctx, userID)
	if err != nil {
		return nil, fmt.Errorf("repository lookup failed: %w", err)
	}
	return mappings, nil
}

// DeleteMapping removes an address mapping by its ID.
func (s *TranslateService) DeleteMapping(ctx context.Context, id string) error {
	if err := s.repo.Delete(ctx, id); err != nil {
		return fmt.Errorf("failed to delete mapping: %w", err)
	}
	return nil
}
