package com.honey.honey.service;

import com.honey.honey.config.TelegramProperties;
import com.honey.honey.dto.TelegramSendResult;
import com.honey.honey.model.NotificationAudit;
import com.honey.honey.model.UserA;
import com.honey.honey.repository.NotificationAuditRepository;
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.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Broadcasts a notification (text + optional image or video) to users via Telegram.
 * Runs asynchronously; respects Telegram rate limit (~30 msg/s) and a stop flag.
 * No @Transactional on the send loop; users are read in pages (500) to avoid memory issues.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class NotificationBroadcastService {

    /** Delay between sends to stay under 30 messages per second (Telegram limit). */
    private static final long DELAY_MS_BETWEEN_SENDS = 40;
    private static final int PAGE_SIZE = 500;

    private final TelegramProperties telegramProperties;
    private final TelegramBotApiService telegramBotApiService;
    private final UserARepository userARepository;
    private final NotificationAuditRepository notificationAuditRepository;
    private final FeatureSwitchService featureSwitchService;
    private final UserService userService;

    private final AtomicBoolean stopRequested = new AtomicBoolean(false);

    /**
     * Request running broadcast to stop. Checked between each user send.
     */
    public void requestStop() {
        stopRequested.set(true);
    }

    /** Mini app URL for the optional inline button (same as in TelegramWebhookController). */
    private static final String MINI_APP_URL = "https://testforapp.website/test/auth";

    /**
     * Run broadcast asynchronously. Uses userIdFrom/userIdTo (internal user ids); if null, uses 1 and max id.
     * Only one of imageUrl or videoUrl is used; video takes priority if both are set.
     * If buttonText is non-empty, each message gets an inline button with that text opening the mini app.
     * When ignoreBlocked is true, skips users with {@code bot_active = false} on {@code db_users_a}.
     */
    @Async
    public void runBroadcast(String message, String imageUrl, String videoUrl,
                            Integer userIdFrom, Integer userIdTo, String buttonText, Boolean ignoreBlocked) {
        stopRequested.set(false);
        String botToken = telegramProperties.getBotToken();
        if (!StringUtils.hasText(botToken)) {
            log.error("Notification broadcast skipped: bot token not configured");
            return;
        }

        int fromId = userIdFrom != null && userIdFrom > 0 ? userIdFrom : 1;
        int toId = userIdTo != null && userIdTo > 0 ? userIdTo : userARepository.getMaxId();
        if (fromId > toId) {
            log.warn("Notification broadcast: empty range fromId={} toId={}", fromId, toId);
            return;
        }

        boolean skipBlocked = Boolean.TRUE.equals(ignoreBlocked);
        log.info("Notification broadcast started: user id range [{}, {}], ignoreBlocked={}", fromId, toId, skipBlocked);
        int pageNumber = 0;
        boolean hasNext = true;
        long sent = 0;
        long failed = 0;

        while (hasNext && !stopRequested.get()) {
            Pageable pageable = PageRequest.of(pageNumber, PAGE_SIZE);
            Page<UserA> page = skipBlocked
                    ? userARepository.findByIdBetweenAndBotActiveTrue(fromId, toId, pageable)
                    : userARepository.findByIdBetween(fromId, toId, pageable);
            for (UserA user : page.getContent()) {
                if (stopRequested.get()) break;
                if (user.getTelegramId() == null) continue;

                TelegramSendResult result = sendOne(botToken, user.getTelegramId(), message, imageUrl, videoUrl, buttonText);
                int statusCode = result.getStatusCode();
                boolean success = result.isSuccess();
                if (success) sent++; else failed++;

                NotificationAudit audit = NotificationAudit.builder()
                        .userId(user.getId())
                        .status(success ? NotificationAudit.STATUS_SUCCESS : NotificationAudit.STATUS_FAILED)
                        .telegramStatusCode(statusCode != 0 ? statusCode : null)
                        .createdAt(Instant.now())
                        .build();
                notificationAuditRepository.save(audit);

                if (!success && statusCode == 403) {
                    userService.setBotActiveByUserId(user.getId(), false);
                }

                try {
                    Thread.sleep(DELAY_MS_BETWEEN_SENDS);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.warn("Broadcast interrupted");
                    return;
                }
            }
            hasNext = page.hasNext();
            pageNumber++;
        }

        if (stopRequested.get()) {
            log.info("Notification broadcast stopped by request. Sent={}, Failed={}", sent, failed);
        } else {
            log.info("Notification broadcast finished. Sent={}, Failed={}", sent, failed);
        }
    }

    /**
     * Normalize message so Telegram renders it correctly: literal \n and \" (e.g. from admin panel paste)
     * become real newline and quote. parse_mode=HTML is sent separately.
     */
    private static String normalizeMessage(String message) {
        if (message == null || message.isEmpty()) return message;
        return message.replace("\\n", "\n").replace("\\\"", "\"");
    }

    /**
     * Send one notification to a chat: text only, or photo+caption, or video+caption (video over image if both set).
     * If buttonText is non-empty, adds an inline keyboard with one button (Web App -> mini app).
     */
    private TelegramSendResult sendOne(String botToken, Long chatId, String message,
                                      String imageUrl, String videoUrl, String buttonText) {
        String normalizedMessage = normalizeMessage(message);
        String baseUrl = "https://api.telegram.org/bot" + botToken;
        Map<String, Object> body = new HashMap<>();
        body.put("chat_id", chatId);
        body.put("parse_mode", "HTML");
        String url;
        if (StringUtils.hasText(videoUrl)) {
            url = baseUrl + "/sendVideo";
            body.put("video", videoUrl);
            if (StringUtils.hasText(normalizedMessage)) body.put("caption", normalizedMessage);
        } else if (StringUtils.hasText(imageUrl)) {
            url = baseUrl + "/sendPhoto";
            body.put("photo", imageUrl);
            if (StringUtils.hasText(normalizedMessage)) body.put("caption", normalizedMessage);
        } else {
            url = baseUrl + "/sendMessage";
            body.put("text", StringUtils.hasText(normalizedMessage) ? normalizedMessage : "(no text)");
        }

        if (StringUtils.hasText(buttonText) && featureSwitchService.isStartGameButtonEnabled()) {
            Map<String, Object> webApp = new HashMap<>();
            webApp.put("url", MINI_APP_URL);
            Map<String, Object> button = new HashMap<>();
            button.put("text", buttonText.trim());
            button.put("web_app", webApp);
            body.put("reply_markup", Map.of("inline_keyboard", java.util.List.of(java.util.List.of(button))));
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
        return telegramBotApiService.postForBroadcast(url, entity);
    }
}
