package com.honey.honey.service;

import com.honey.honey.dto.*;
import com.honey.honey.exception.GameException;
import com.honey.honey.model.SupportMessage;
import com.honey.honey.model.SupportTicket;
import com.honey.honey.model.SupportTicket.TicketStatus;
import com.honey.honey.model.UserA;
import com.honey.honey.repository.SupportMessageRepository;
import com.honey.honey.repository.SupportTicketRepository;
import com.honey.honey.repository.UserARepository;
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.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class SupportTicketService {

    private static final int MAX_OPENED_TICKETS = 5;
    private static final int MAX_MESSAGES_PER_TICKET = 20;
    private static final int RATE_LIMIT_SECONDS = 10;

    private final SupportTicketRepository ticketRepository;
    private final SupportMessageRepository messageRepository;
    private final UserARepository userARepository;
    private final LocalizationService localizationService;

    /**
     * Creates a new support ticket with the first message.
     * Validates limits and rate limiting.
     */
    @Transactional
    public TicketDto createTicket(Integer userId, CreateTicketRequest request) {
        // Validate user exists
        UserA user = userARepository.findById(userId)
                .orElseThrow(() -> new GameException(localizationService.getMessage("user.error.notFound")));

        // Check limit: max 5 OPENED tickets per user
        long openedTicketsCount = ticketRepository.countByUserIdAndStatus(userId, TicketStatus.OPENED);
        if (openedTicketsCount >= MAX_OPENED_TICKETS) {
            throw new GameException(localizationService.getMessage("support.error.maxTicketsReached", 
                    String.valueOf(MAX_OPENED_TICKETS)));
        }

        // Create ticket
        SupportTicket ticket = SupportTicket.builder()
                .user(user)
                .subject(request.getSubject().trim())
                .status(TicketStatus.OPENED)
                .build();

        ticket = ticketRepository.save(ticket);

        // Create first message
        SupportMessage message = SupportMessage.builder()
                .ticket(ticket)
                .user(user)
                .message(request.getMessage().trim())
                .build();

        messageRepository.save(message);

        log.info("Created support ticket {} for user {}", ticket.getId(), userId);

        return mapToTicketDto(ticket, 1);
    }

    /**
     * Adds a message to an existing ticket.
     * Validates ticket status, message limit, and rate limiting.
     */
    @Transactional
    public MessageDto addMessage(Integer userId, Long ticketId, CreateMessageRequest request) {
        // Validate user exists
        UserA user = userARepository.findById(userId)
                .orElseThrow(() -> new GameException(localizationService.getMessage("user.error.notFound")));

        // Get ticket (user can only access their own tickets)
        SupportTicket ticket = ticketRepository.findByIdAndUserId(ticketId, userId)
                .orElseThrow(() -> new GameException(localizationService.getMessage("support.error.ticketNotFound")));

        // Check if ticket is closed
        if (ticket.getStatus() == TicketStatus.CLOSED) {
            throw new GameException(localizationService.getMessage("support.error.ticketClosed"));
        }

        // Check limit: max 20 messages per OPENED ticket
        long messageCount = messageRepository.countByTicketId(ticketId);
        if (messageCount >= MAX_MESSAGES_PER_TICKET) {
            throw new GameException(localizationService.getMessage("support.error.maxMessagesReached", 
                    String.valueOf(MAX_MESSAGES_PER_TICKET)));
        }

        // Rate limiting: not more than once per 10 seconds
        List<SupportMessage> lastMessages = messageRepository.findLastMessageByTicketIdAndUserId(
                ticketId, userId, PageRequest.of(0, 1));
        if (!lastMessages.isEmpty()) {
            SupportMessage lastMessage = lastMessages.get(0);
            long secondsSinceLastMessage = ChronoUnit.SECONDS.between(lastMessage.getCreatedAt(), Instant.now());
            if (secondsSinceLastMessage < RATE_LIMIT_SECONDS) {
                long remainingSeconds = RATE_LIMIT_SECONDS - secondsSinceLastMessage;
                throw new GameException(localizationService.getMessage("support.error.rateLimitWait", 
                        String.valueOf(remainingSeconds)));
            }
        }

        // Create message
        SupportMessage message = SupportMessage.builder()
                .ticket(ticket)
                .user(user)
                .message(request.getMessage().trim())
                .build();

        message = messageRepository.save(message);

        log.info("Added message {} to ticket {} for user {}", message.getId(), ticketId, userId);

        return mapToMessageDto(message, ticket.getUser().getId());
    }

    /**
     * Closes a ticket.
     */
    @Transactional
    public void closeTicket(Integer userId, Long ticketId) {
        // Get ticket (user can only access their own tickets)
        SupportTicket ticket = ticketRepository.findByIdAndUserId(ticketId, userId)
                .orElseThrow(() -> new GameException(localizationService.getMessage("support.error.ticketNotFound")));

        if (ticket.getStatus() == TicketStatus.CLOSED) {
            throw new GameException(localizationService.getMessage("support.error.ticketAlreadyClosed"));
        }

        ticket.setStatus(TicketStatus.CLOSED);
        ticketRepository.save(ticket);

        log.info("Closed ticket {} for user {}", ticketId, userId);
    }

    /**
     * Gets ticket details with all messages.
     */
    @Transactional(readOnly = true)
    public TicketDetailDto getTicketDetail(Integer userId, Long ticketId) {
        // Get ticket (user can only access their own tickets)
        SupportTicket ticket = ticketRepository.findByIdAndUserId(ticketId, userId)
                .orElseThrow(() -> new GameException(localizationService.getMessage("support.error.ticketNotFound")));

        // Get all messages
        List<SupportMessage> messages = messageRepository.findByTicketIdOrderByCreatedAtAsc(ticketId);

        // Map to DTOs
        List<MessageDto> messageDtos = messages.stream()
                .map(msg -> mapToMessageDto(msg, ticket.getUser().getId()))
                .collect(Collectors.toList());

        return TicketDetailDto.builder()
                .id(ticket.getId())
                .subject(ticket.getSubject())
                .status(ticket.getStatus())
                .createdAt(ticket.getCreatedAt())
                .updatedAt(ticket.getUpdatedAt())
                .messages(messageDtos)
                .build();
    }

    /**
     * Gets ticket history for a user (last 20 tickets).
     */
    @Transactional(readOnly = true)
    public List<TicketDto> getTicketHistory(Integer userId) {
        Pageable pageable = PageRequest.of(0, 20);
        Page<SupportTicket> tickets = ticketRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable);

        return tickets.getContent().stream()
                .map(ticket -> {
                    long messageCount = messageRepository.countByTicketId(ticket.getId());
                    return mapToTicketDto(ticket, (int) messageCount);
                })
                .collect(Collectors.toList());
    }

    /**
     * Maps SupportTicket to TicketDto.
     */
    private TicketDto mapToTicketDto(SupportTicket ticket, int messageCount) {
        return TicketDto.builder()
                .id(ticket.getId())
                .subject(ticket.getSubject())
                .status(ticket.getStatus())
                .createdAt(ticket.getCreatedAt())
                .updatedAt(ticket.getUpdatedAt())
                .messageCount(messageCount)
                .build();
    }

    /**
     * Maps SupportMessage to MessageDto.
     * isFromSupport is true if the message is from a different user than the ticket owner.
     */
    private MessageDto mapToMessageDto(SupportMessage message, Integer ticketOwnerUserId) {
        boolean isFromSupport = !message.getUser().getId().equals(ticketOwnerUserId);
        
        return MessageDto.builder()
                .id(message.getId())
                .ticketId(message.getTicket().getId())
                .userId(message.getUser().getId())
                .message(message.getMessage())
                .createdAt(message.getCreatedAt())
                .isFromSupport(isFromSupport)
                .build();
    }
}

