package kz.konturai.parser.service;

import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.io.SyndFeedInput;
import com.rometools.rome.io.XmlReader;
import kz.konturai.parser.model.MarketItem;
import kz.konturai.parser.repository.MarketItemRepository;
import org.jsoup.Jsoup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;

@Service
public class KursivParserService implements ParserService {

    private static final Logger logger = LoggerFactory.getLogger(KursivParserService.class);

    @Autowired
    private MarketItemRepository marketItemRepository;

    @Autowired
    private OpenAIAnalyticsService analyticsService;

    @Value("${rss.feed.url}")
    private String rssFeedUrl;

    @Value("${parser.scheduler.enabled:true}")
    private boolean schedulerEnabled;

    @Override
    public String getSourceName() {
        return "kursiv";
    }

    /**
     * Автоматический запуск парсинга каждые 30 минут
     * Cron выражение: "0 0/30 * * * ?" означает запуск в 0 и 30 минут каждого часа
     */
    @Scheduled(cron = "0 0/5 * * * ?")
    public void scheduledParseAndStoreNews() {
        if (!schedulerEnabled) {
            logger.debug("⏸️ Планировщик отключен. Пропуск планового парсинга Kursiv.media");
            return;
        }
        logger.info("🔄 Запуск планового парсинга новостей с Kursiv.media...");
        try {
            List<MarketItem> savedItems = parseAndSaveRssFeed();
            logger.info("✅ Плановый парсинг завершен. Обработано записей: {}", savedItems.size());
        } catch (Exception e) {
            logger.error("❌ Ошибка при плановом парсинге", e);
        }
    }

    /**
     * Основной метод для парсинга RSS-ленты и сохранения данных в MongoDB
     */
    public List<MarketItem> parseAndSaveRssFeed() {
        List<MarketItem> savedItems = new ArrayList<>();

        try {
            logger.info("Начинаем парсинг RSS-ленты: {}", rssFeedUrl);

            // Получаем RSS-ленту
            SyndFeed feed = getRssFeed();

            if (feed == null) {
                logger.error("Не удалось получить RSS-ленту");
                return savedItems;
            }

            logger.info("Получено {} записей из RSS-ленты", feed.getEntries().size());

            // Обрабатываем каждую запись
            for (SyndEntry entry : feed.getEntries()) {
                try {
                    MarketItem marketItem = processRssEntry(entry);
                    if (marketItem != null) {
                        try {
                            MarketItem.Analytics analytics = analyticsService.analyzeText(marketItem.getRawText());
                            marketItem.setAnalytics(analytics);
                        } catch (Exception e) {
                            logger.warn("Analytics step failed for entry '{}': {}", marketItem.getTitle(),
                                    e.getMessage());
                        }
                        MarketItem savedItem = saveOrUpdateMarketItem(marketItem);
                        if (savedItem != null) {
                            savedItems.add(savedItem);
                        }
                    }
                } catch (Exception e) {
                    logger.error("Ошибка при обработке записи: {}", entry.getTitle(), e);
                }
            }

            logger.info("Успешно обработано {} записей", savedItems.size());

        } catch (Exception e) {
            logger.error("Ошибка при парсинге RSS-ленты", e);
        }

        return savedItems;
    }

    /**
     * Получение RSS-ленты
     */
    private SyndFeed getRssFeed() throws Exception {
        try {
            URL feedUrl = URI.create(rssFeedUrl).toURL();
            SyndFeedInput input = new SyndFeedInput();

            // Добавляем таймаут и обработку ошибок
            java.net.URLConnection connection = feedUrl.openConnection();
            connection.setConnectTimeout(10000); // 10 секунд
            connection.setReadTimeout(30000); // 30 секунд
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");

            try (XmlReader reader = new XmlReader(connection.getInputStream())) {
                return input.build(reader);
            }
        } catch (com.rometools.rome.io.ParsingFeedException e) {
            logger.error("Ошибка парсинга XML для Kursiv: {}", e.getMessage());
            throw new Exception("Невалидный XML в RSS-ленте Kursiv: " + e.getMessage(), e);
        } catch (java.net.UnknownHostException e) {
            logger.error("Не удается подключиться к серверу Kursiv: {}", e.getMessage());
            throw new Exception("Сервер Kursiv недоступен: " + e.getMessage(), e);
        } catch (java.net.SocketTimeoutException e) {
            logger.error("Таймаут при подключении к Kursiv: {}", e.getMessage());
            throw new Exception("Таймаут подключения к Kursiv: " + e.getMessage(), e);
        } catch (Exception e) {
            logger.error("Общая ошибка при получении RSS-ленты Kursiv: {}", e.getMessage());
            throw new Exception("Ошибка получения RSS-ленты Kursiv: " + e.getMessage(), e);
        }
    }

    /**
     * Обработка отдельной записи RSS
     */
    private MarketItem processRssEntry(SyndEntry entry) {
        try {
            // Извлекаем основные поля
            String title = entry.getTitle();
            String url = entry.getLink();
            String description = getDescription(entry);

            if (title == null || url == null) {
                logger.warn("Пропускаем запись с отсутствующими обязательными полями: title={}, url={}", title, url);
                return null;
            }

            // Очищаем текст от HTML
            String cleanText = cleanHtmlText(description);

            // Преобразуем дату
            LocalDateTime publishedAt = convertToLocalDateTime(entry.getPublishedDate());

            // Генерируем хэш
            String hash = generateHash(url, title);

            // Создаем объект MarketItem
            MarketItem marketItem = new MarketItem(url, title, publishedAt, cleanText, hash);

            logger.debug("Обработана запись: {}", title);

            return marketItem;

        } catch (Exception e) {
            logger.error("Ошибка при обработке записи RSS", e);
            return null;
        }
    }

    /**
     * Извлечение описания из записи RSS
     */
    private String getDescription(SyndEntry entry) {
        if (entry.getDescription() != null) {
            return entry.getDescription().getValue();
        }

        // Пробуем получить содержимое из других полей
        if (entry.getContents() != null && !entry.getContents().isEmpty()) {
            return entry.getContents().get(0).getValue();
        }

        return "";
    }

    /**
     * Очистка HTML-тегов из текста
     */
    private String cleanHtmlText(String htmlText) {
        if (htmlText == null || htmlText.trim().isEmpty()) {
            return "";
        }

        try {
            return Jsoup.parse(htmlText).text();
        } catch (Exception e) {
            logger.warn("Ошибка при очистке HTML: {}", e.getMessage());
            return htmlText;
        }
    }

    /**
     * Преобразование даты в LocalDateTime
     */
    private LocalDateTime convertToLocalDateTime(java.util.Date date) {
        if (date == null) {
            return LocalDateTime.now();
        }

        return date.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
    }

    /**
     * Генерация SHA-256 хэша для дедупликации
     */
    private String generateHash(String url, String title) {
        try {
            String input = url + "::" + title;
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));

            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }

            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            logger.error("Ошибка при генерации хэша", e);
            return String.valueOf((url + "::" + title).hashCode());
        }
    }

    /**
     * Сохранение или обновление записи в MongoDB (upsert)
     */
    private MarketItem saveOrUpdateMarketItem(MarketItem marketItem) {
        try {
            // Проверяем существование записи по хэшу
            MarketItem existingItem = marketItemRepository.findByHash(marketItem.getHash()).orElse(null);

            if (existingItem != null) {
                // Обновляем существующую запись
                existingItem.setTitle(marketItem.getTitle());
                existingItem.setUrl(marketItem.getUrl());
                existingItem.setPublishedAt(marketItem.getPublishedAt());
                existingItem.setRawText(marketItem.getRawText());
                existingItem.setAddedAt(LocalDateTime.now()); // Обновляем время добавления

                MarketItem savedItem = marketItemRepository.save(existingItem);
                logger.debug("Обновлена существующая запись: {}", marketItem.getTitle());
                return savedItem;
            } else {
                // Создаем новую запись
                MarketItem savedItem = marketItemRepository.save(marketItem);
                logger.debug("Создана новая запись: {}", marketItem.getTitle());
                return savedItem;
            }

        } catch (org.springframework.data.mongodb.UncategorizedMongoDbException e) {
            if (e.getMessage().contains("Unauthorized") || e.getMessage().contains("authentication")) {
                logger.error("❌ Ошибка аутентификации MongoDB. Проверьте учетные данные в application.properties");
            } else {
                logger.error("❌ Ошибка подключения к MongoDB: {}", e.getMessage());
            }
            return null;
        } catch (Exception e) {
            logger.error("Ошибка при сохранении записи: {}", marketItem.getTitle(), e);
            return null;
        }
    }

    /**
     * Получение статистики по сохраненным записям
     */
    public long getTotalItemsCount() {
        return marketItemRepository.count();
    }

    /**
     * Получение всех сохраненных записей
     */
    public List<MarketItem> getAllItems() {
        return marketItemRepository.findAll();
    }
}
