package com.screenshot.server.bot;

import com.screenshot.server.config.TelegramBotConfig;
import com.screenshot.server.service.StorageService;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.methods.send.SendDocument;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.InputFile;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class ScreenshotBot extends TelegramLongPollingBot {

    private final TelegramBotConfig botConfig;
    private final StorageService storageService;

    // Состояния пользователей
    private final Map<Long, UserSession> userSessions = new ConcurrentHashMap<>();

    private static final String WELCOME_MESSAGE = """
        🖼 *Screenshot Storage Bot*

        ⚠️ *Только для частного использования*
        Этот бот предназначен исключительно для авторизованных пользователей.

        📋 *Доступные команды:*
        /start - Начало работы
        /dates - Показать доступные даты
        /download - Скачать скриншоты
        /status - Статус хранилища
        /help - Помощь
        """;

    private static final String UNAUTHORIZED_MESSAGE = """
        ⛔ *Доступ запрещён*

        Вы не авторизованы для использования этого бота.
        Ваш Chat ID: `%d`

        Обратитесь к администратору для получения доступа.
        """;

    public ScreenshotBot(TelegramBotConfig botConfig, StorageService storageService) {
        super(botConfig.getToken());
        this.botConfig = botConfig;
        this.storageService = storageService;
    }

    @Override
    public String getBotUsername() {
        return botConfig.getUsername();
    }

    @Override
    public void onUpdateReceived(Update update) {
        if (update.hasMessage() && update.getMessage().hasText()) {
            handleTextMessage(update);
        } else if (update.hasCallbackQuery()) {
            handleCallbackQuery(update);
        }
    }

    private void handleTextMessage(Update update) {
        Long chatId = update.getMessage().getChatId();
        String text = update.getMessage().getText();

        // Проверка авторизации
        if (!botConfig.isUserAllowed(chatId)) {
            sendUnauthorizedMessage(chatId);
            return;
        }

        // Проверяем, ожидаем ли ввод времени
        UserSession session = userSessions.get(chatId);
        if (session != null && session.state == SessionState.AWAITING_TIME_RANGE) {
            handleTimeRangeInput(chatId, text, session);
            return;
        }

        // Обработка команд
        switch (text) {
            case "/start", "/help" -> sendWelcomeMessage(chatId);
            case "/dates" -> sendAvailableDates(chatId);
            case "/download" -> startDownloadFlow(chatId);
            case "/status" -> sendStorageStatus(chatId);
            default -> sendMessage(chatId, "Неизвестная команда. Используйте /help");
        }
    }

    private void handleCallbackQuery(Update update) {
        Long chatId = update.getCallbackQuery().getMessage().getChatId();
        String data = update.getCallbackQuery().getData();

        if (!botConfig.isUserAllowed(chatId)) {
            sendUnauthorizedMessage(chatId);
            return;
        }

        if (data.startsWith("date:")) {
            handleDateSelection(chatId, data.substring(5));
        } else if (data.startsWith("period:")) {
            handlePeriodSelection(chatId, data.substring(7));
        } else if (data.equals("all_day")) {
            downloadAllDay(chatId);
        } else if (data.equals("custom_time")) {
            askForTimeRange(chatId);
        } else if (data.equals("cancel")) {
            cancelOperation(chatId);
        }
    }

    private void sendWelcomeMessage(Long chatId) {
        sendMarkdownMessage(chatId, WELCOME_MESSAGE);
    }

    private void sendUnauthorizedMessage(Long chatId) {
        sendMarkdownMessage(chatId, String.format(UNAUTHORIZED_MESSAGE, chatId));
    }

    private void sendAvailableDates(Long chatId) {
        List<LocalDate> dates = storageService.getAvailableDates();

        if (dates.isEmpty()) {
            sendMessage(chatId, "📭 Нет доступных скриншотов");
            return;
        }

        StringBuilder sb = new StringBuilder("📅 *Доступные даты:*\n\n");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy (EEEE)", new Locale("ru"));

        int count = 0;
        for (LocalDate date : dates) {
            if (count++ >= 30) {
                sb.append("\n_...и ещё ").append(dates.size() - 30).append(" дат_");
                break;
            }
            int filesCount = storageService.getFilesForDate(date).size();
            sb.append("• `").append(date).append("` - ")
              .append(date.format(formatter))
              .append(" (").append(filesCount).append(" файлов)\n");
        }

        sendMarkdownMessage(chatId, sb.toString());
    }

    private void startDownloadFlow(Long chatId) {
        List<LocalDate> dates = storageService.getAvailableDates();

        if (dates.isEmpty()) {
            sendMessage(chatId, "📭 Нет доступных скриншотов для скачивания");
            return;
        }

        // Создаём клавиатуру с датами (последние 10)
        InlineKeyboardMarkup markup = new InlineKeyboardMarkup();
        List<List<InlineKeyboardButton>> keyboard = new ArrayList<>();

        int count = 0;
        List<InlineKeyboardButton> row = new ArrayList<>();

        for (LocalDate date : dates) {
            if (count++ >= 12) break;

            InlineKeyboardButton button = new InlineKeyboardButton();
            button.setText(date.format(DateTimeFormatter.ofPattern("dd.MM")));
            button.setCallbackData("date:" + date);
            row.add(button);

            if (row.size() == 3) {
                keyboard.add(new ArrayList<>(row));
                row.clear();
            }
        }

        if (!row.isEmpty()) {
            keyboard.add(row);
        }

        // Кнопка отмены
        List<InlineKeyboardButton> cancelRow = new ArrayList<>();
        InlineKeyboardButton cancelBtn = new InlineKeyboardButton();
        cancelBtn.setText("❌ Отмена");
        cancelBtn.setCallbackData("cancel");
        cancelRow.add(cancelBtn);
        keyboard.add(cancelRow);

        markup.setKeyboard(keyboard);

        sendMessageWithKeyboard(chatId, "📅 Выберите дату:", markup);
    }

    private void handleDateSelection(Long chatId, String dateStr) {
        try {
            LocalDate date = LocalDate.parse(dateStr);

            // Сохраняем выбранную дату в сессию
            UserSession session = new UserSession();
            session.selectedDate = date;
            session.state = SessionState.DATE_SELECTED;
            userSessions.put(chatId, session);

            int filesCount = storageService.getFilesForDate(date).size();

            // Предлагаем выбрать период
            InlineKeyboardMarkup markup = new InlineKeyboardMarkup();
            List<List<InlineKeyboardButton>> keyboard = new ArrayList<>();

            // Весь день
            List<InlineKeyboardButton> row1 = new ArrayList<>();
            InlineKeyboardButton allDayBtn = new InlineKeyboardButton();
            allDayBtn.setText("📆 Весь день (" + filesCount + " файлов)");
            allDayBtn.setCallbackData("all_day");
            row1.add(allDayBtn);
            keyboard.add(row1);

            // Предустановленные периоды
            String[][] periods = {
                {"🌅 Утро (06-12)", "period:06:00-12:00"},
                {"☀️ День (12-18)", "period:12:00-18:00"},
                {"🌙 Вечер (18-00)", "period:18:00-23:59"}
            };

            for (String[] period : periods) {
                List<InlineKeyboardButton> row = new ArrayList<>();
                InlineKeyboardButton btn = new InlineKeyboardButton();
                btn.setText(period[0]);
                btn.setCallbackData(period[1]);
                row.add(btn);
                keyboard.add(row);
            }

            // Свой период
            List<InlineKeyboardButton> customRow = new ArrayList<>();
            InlineKeyboardButton customBtn = new InlineKeyboardButton();
            customBtn.setText("⏰ Указать время");
            customBtn.setCallbackData("custom_time");
            customRow.add(customBtn);
            keyboard.add(customRow);

            // Отмена
            List<InlineKeyboardButton> cancelRow = new ArrayList<>();
            InlineKeyboardButton cancelBtn = new InlineKeyboardButton();
            cancelBtn.setText("❌ Отмена");
            cancelBtn.setCallbackData("cancel");
            cancelRow.add(cancelBtn);
            keyboard.add(cancelRow);

            markup.setKeyboard(keyboard);

            String message = String.format("📅 Выбрана дата: *%s*\n\nВыберите период:", 
                date.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));

            sendMarkdownMessageWithKeyboard(chatId, message, markup);

        } catch (DateTimeParseException e) {
            sendMessage(chatId, "❌ Ошибка парсинга даты");
        }
    }

    private void handlePeriodSelection(Long chatId, String periodStr) {
        UserSession session = userSessions.get(chatId);
        if (session == null || session.selectedDate == null) {
            sendMessage(chatId, "❌ Сначала выберите дату. Используйте /download");
            return;
        }

        String[] parts = periodStr.split("-");
        LocalTime startTime = LocalTime.parse(parts[0]);
        LocalTime endTime = LocalTime.parse(parts[1]);

        downloadFilesForPeriod(chatId, session.selectedDate, startTime, endTime);
    }

    private void downloadAllDay(Long chatId) {
        UserSession session = userSessions.get(chatId);
        if (session == null || session.selectedDate == null) {
            sendMessage(chatId, "❌ Сначала выберите дату");
            return;
        }

        downloadFilesForPeriod(chatId, session.selectedDate, LocalTime.MIN, LocalTime.MAX);
    }

    private void askForTimeRange(Long chatId) {
        UserSession session = userSessions.get(chatId);
        if (session == null) {
            session = new UserSession();
            userSessions.put(chatId, session);
        }
        session.state = SessionState.AWAITING_TIME_RANGE;

        sendMessage(chatId, """
            ⏰ Введите период времени в формате:
            `HH:MM-HH:MM`

            Например: `09:00-18:30`

            Для отмены отправьте /cancel
            """);
    }

    private void handleTimeRangeInput(Long chatId, String text, UserSession session) {
        if (text.equals("/cancel")) {
            cancelOperation(chatId);
            return;
        }

        try {
            String[] parts = text.split("-");
            if (parts.length != 2) {
                sendMessage(chatId, "❌ Неверный формат. Используйте HH:MM-HH:MM");
                return;
            }

            LocalTime startTime = LocalTime.parse(parts[0].trim());
            LocalTime endTime = LocalTime.parse(parts[1].trim());

            if (session.selectedDate == null) {
                sendMessage(chatId, "❌ Дата не выбрана. Используйте /download");
                return;
            }

            downloadFilesForPeriod(chatId, session.selectedDate, startTime, endTime);

        } catch (DateTimeParseException e) {
            sendMessage(chatId, "❌ Неверный формат времени. Используйте HH:MM-HH:MM");
        }
    }

    private void downloadFilesForPeriod(Long chatId, LocalDate date, LocalTime startTime, LocalTime endTime) {
        sendMessage(chatId, "⏳ Подготовка архива...");

        try {
            List<Path> files = storageService.getFilesForDateAndPeriod(date, startTime, endTime);

            if (files.isEmpty()) {
                sendMessage(chatId, "📭 Нет файлов за указанный период");
                userSessions.remove(chatId);
                return;
            }

            // Создаём ZIP архивы
            List<Path> archives = storageService.createZipArchives(
                files, 
                botConfig.getMaxFileSizeBytes()
            );

            sendMessage(chatId, String.format(
                "📦 Найдено %d файлов. Отправка %d архива(ов)...", 
                files.size(), archives.size()
            ));

            // Отправляем архивы
            for (int i = 0; i < archives.size(); i++) {
                Path archive = archives.get(i);
                String caption = String.format("📦 Архив %d/%d - %s (%s)", 
                    i + 1, archives.size(),
                    date.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")),
                    storageService.formatSize(Files.size(archive))
                );

                sendDocument(chatId, archive.toFile(), caption);

                // Удаляем временный файл
                Files.deleteIfExists(archive);
            }

            sendMessage(chatId, "✅ Отправка завершена!");

        } catch (Exception e) {
            sendMessage(chatId, "❌ Ошибка: " + e.getMessage());
            e.printStackTrace();
        } finally {
            userSessions.remove(chatId);
        }
    }

    private void sendStorageStatus(Long chatId) {
        StorageService.StorageInfo info = storageService.getStorageInfo();
        List<LocalDate> dates = storageService.getAvailableDates();

        String message = String.format("""
            📊 *Статус хранилища*

            📁 Путь: `%s`
            📄 Всего файлов: %d
            💾 Размер: %s
            📅 Дат с данными: %d
            """, 
            info.path(),
            info.fileCount(),
            info.formattedSize(),
            dates.size()
        );

        sendMarkdownMessage(chatId, message);
    }

    private void cancelOperation(Long chatId) {
        userSessions.remove(chatId);
        sendMessage(chatId, "❌ Операция отменена");
    }

    // === Вспомогательные методы отправки ===

    private void sendMessage(Long chatId, String text) {
        try {
            SendMessage message = new SendMessage();
            message.setChatId(chatId);
            message.setText(text);
            execute(message);
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }

    private void sendMarkdownMessage(Long chatId, String text) {
        try {
            SendMessage message = new SendMessage();
            message.setChatId(chatId);
            message.setText(text);
            message.setParseMode("Markdown");
            execute(message);
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }

    private void sendMessageWithKeyboard(Long chatId, String text, InlineKeyboardMarkup markup) {
        try {
            SendMessage message = new SendMessage();
            message.setChatId(chatId);
            message.setText(text);
            message.setReplyMarkup(markup);
            execute(message);
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }

    private void sendMarkdownMessageWithKeyboard(Long chatId, String text, InlineKeyboardMarkup markup) {
        try {
            SendMessage message = new SendMessage();
            message.setChatId(chatId);
            message.setText(text);
            message.setParseMode("Markdown");
            message.setReplyMarkup(markup);
            execute(message);
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }

    private void sendDocument(Long chatId, File file, String caption) {
        try {
            SendDocument sendDocument = new SendDocument();
            sendDocument.setChatId(chatId);
            sendDocument.setDocument(new InputFile(file));
            sendDocument.setCaption(caption);
            execute(sendDocument);
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }

    // === Вспомогательные классы ===

    private enum SessionState {
        NONE,
        DATE_SELECTED,
        AWAITING_TIME_RANGE
    }

    private static class UserSession {
        SessionState state = SessionState.NONE;
        LocalDate selectedDate;
    }
}
