package com.honey.honey.service;

import com.honey.honey.dto.TransactionDto;
import com.honey.honey.model.Transaction;
import com.honey.honey.repository.TransactionRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Service for managing transaction records.
 * Creates transaction entries for all user balance changes.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TransactionService {

    private final TransactionRepository transactionRepository;
    private final LocalizationService localizationService;

    /**
     * Creates a deposit transaction.
     * 
     * @param userId User ID
     * @param amount Amount in bigint format (positive)
     */
    @Transactional
    public void createDepositTransaction(Integer userId, Long amount) {
        Transaction transaction = Transaction.builder()
                .userId(userId)
                .amount(amount)
                .type(Transaction.TransactionType.DEPOSIT)
                .build();
        transactionRepository.save(transaction);
        log.debug("Created deposit transaction: userId={}, amount={}", userId, amount);
    }

    /**
     * Creates a withdrawal transaction.
     * 
     * @param userId User ID
     * @param amount Amount in bigint format (positive, will be stored as negative)
     */
    @Transactional
    public void createWithdrawalTransaction(Integer userId, Long amount) {
        Transaction transaction = Transaction.builder()
                .userId(userId)
                .amount(-amount) // Store as negative
                .type(Transaction.TransactionType.WITHDRAWAL)
                .build();
        transactionRepository.save(transaction);
        log.debug("Created withdrawal transaction: userId={}, amount={}", userId, amount);
    }

    /**
     * Creates a task bonus transaction.
     * 
     * @param userId User ID
     * @param amount Amount in bigint format (positive)
     * @param taskId Task ID
     */
    @Transactional
    public void createTaskBonusTransaction(Integer userId, Long amount, Integer taskId) {
        Transaction transaction = Transaction.builder()
                .userId(userId)
                .amount(amount)
                .type(Transaction.TransactionType.TASK_BONUS)
                .taskId(taskId)
                .build();
        transactionRepository.save(transaction);
        log.debug("Created task bonus transaction: userId={}, amount={}, taskId={}", userId, amount, taskId);
    }

    /**
     * Creates a cancellation of withdrawal transaction.
     * Used when admin cancels a payout - refunds tickets to user.
     * 
     * @param userId User ID
     * @param amount Amount in bigint format (positive, refund amount)
     * @param createdAt Timestamp to use (from payout resolved_at)
     */
    @Transactional
    public void createCancellationOfWithdrawalTransaction(Integer userId, Long amount, Instant createdAt) {
        Transaction transaction = Transaction.builder()
                .userId(userId)
                .amount(amount) // Positive amount (credit back to user)
                .type(Transaction.TransactionType.CANCELLATION_OF_WITHDRAWAL)
                .taskId(null)
                .createdAt(createdAt) // Set custom timestamp (@PrePersist will check if null)
                .build();
        transactionRepository.save(transaction);
        log.info("Created cancellation of withdrawal transaction: userId={}, amount={}, createdAt={}", 
                userId, amount, createdAt);
    }

    /**
     * Gets transactions for a user with pagination.
     * Returns 50 transactions per page, ordered by creation time descending (newest first).
     * 
     * @param userId User ID
     * @param page Page number (0-indexed)
     * @param timezone Optional timezone (e.g., "Europe/London"). If null, uses UTC.
     * @param languageCode User's language code for localization (e.g., "EN", "RU")
     * @return Page of transactions
     */
    @Transactional(readOnly = true)
    public Page<TransactionDto> getUserTransactions(Integer userId, int page, String timezone, String languageCode) {
        // Hardcoded page size: 50 transactions per page
        Pageable pageable = PageRequest.of(page, 50);
        
        Page<Transaction> transactions = transactionRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable);
        
        // Determine timezone to use
        ZoneId zoneId;
        try {
            zoneId = (timezone != null && !timezone.trim().isEmpty()) 
                    ? ZoneId.of(timezone) 
                    : ZoneId.of("UTC");
        } catch (Exception e) {
            // Invalid timezone, fallback to UTC
            zoneId = ZoneId.of("UTC");
        }
        
        // Get localized "at" word
        String atWord = localizationService.getMessage("dateTime.at", languageCode);
        if (atWord == null || atWord.isEmpty()) {
            atWord = "at"; // Fallback to English
        }
        
        // Create formatter with localized "at" word
        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM '" + atWord + "' HH:mm")
                .withZone(zoneId);
        
        return transactions.map(transaction -> {
            // Format date
            String date = formatter.format(transaction.getCreatedAt());
            
            String typeEnumValue = transaction.getType().name();
            Integer taskIdToInclude = transaction.getTaskId();
            
            return TransactionDto.builder()
                    .amount(transaction.getAmount())
                    .date(date)
                    .type(typeEnumValue)
                    .taskId(taskIdToInclude)
                    .build();
        });
    }

    // Transaction type localization is handled in the frontend.
    // This method is no longer used but kept for reference.
    @Deprecated
    private String mapTransactionTypeToDisplayName(Transaction.TransactionType type, String languageCode) {
        // This method is deprecated - frontend handles all transaction type localization
        return type.name();
    }
}

