package kz.konturai.parser.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.junit.jupiter.api.Test;

class MarketingAnalysisServiceChartPlacementTest {

        // Keep this test free of Spring/Mockito to avoid agent-attach requirements.
        private final MarketingAnalysisService marketingAnalysisService = new MarketingAnalysisService(null, null, null,
                        null, null, null);

        @SuppressWarnings({ "unchecked", "rawtypes" })
        private Map buildPlaceholderMap() throws Exception {
                Class<?> chartMappingClass = Class
                                .forName("kz.konturai.parser.service.MarketingAnalysisService$ChartMapping");
                Constructor<?> ctor = chartMappingClass.getDeclaredConstructor(String.class, String.class);
                ctor.setAccessible(true);

                Map placeholderMap = new HashMap();
                placeholderMap.put("[[CHART_SEASONALITY]]", ctor.newInstance("seasonality", "seasonality"));
                placeholderMap.put("[[CHART_AGE_DISTRIBUTION]]", ctor.newInstance("audienceAge", "audienceAge"));
                placeholderMap.put("[[CHART_GENDER_DISTRIBUTION]]",
                                ctor.newInstance("audienceGender", "audienceGender"));
                placeholderMap.put("[[CHART_MARKET_SHARE]]", ctor.newInstance("marketShareChart", "marketShareChart"));
                placeholderMap.put("[[TABLE_COMPARISON]]", ctor.newInstance("comparisonTable", "comparisonTable"));
                placeholderMap.put("[[CHART_SWOT]]", ctor.newInstance("swot", "swot"));
                placeholderMap.put("[[CHART_CHANNELS]]", ctor.newInstance("channelsPotential", "channelsPotential"));
                placeholderMap.put("[[CHART_CHANNEL_MATRIX]]",
                                ctor.newInstance("segmentsChannelMatrix", "segmentsChannelMatrix"));
                placeholderMap.put("[[CHART_FUNNEL]]", ctor.newInstance("conversionFunnel", "funnel"));
                return placeholderMap;
        }

        private Map<String, Object> buildChartsDataSeasonalityOnly() {
                Map<String, Object> chartsData = new HashMap<>();
                chartsData.put("seasonality",
                                Map.of("labels", java.util.List.of("Jan"), "values", java.util.List.of(1)));
                return chartsData;
        }

        private Map<String, Object> buildChartsDataChannelsAndMatrix() {
                Map<String, Object> chartsData = new HashMap<>();
                chartsData.put("channelsPotential", Map.of("labels", java.util.List.of("Instagram"), "datasets",
                                java.util.List.of(Map.of("label", "Потенциал", "data", java.util.List.of(80)))));
                chartsData.put("segmentsChannelMatrix",
                                java.util.List.of(Map.of("segmentName", "Молодежь", "instagram", "high", "facebook",
                                                "medium")));
                return chartsData;
        }

        @Test
        void addMissingChartsToSections_shouldIgnoreTocListItems_andUseRealMarkdownHeading() throws Exception {
                String report = ""
                                + "## Оглавление\n"
                                + "- [III. Анализ рынка и сезонности](#section-iii)\n"
                                + "- [IV. Анализ целевой аудитории](#section-iv)\n"
                                + "\n"
                                + "## III. Анализ рынка и сезонности\n"
                                + "Тут есть текст про рынок.\n"
                                + "\n"
                                + "## IV. Анализ целевой аудитории\n"
                                + "Тут аудитория.\n";

                Method m = MarketingAnalysisService.class.getDeclaredMethod("addMissingChartsToSections", String.class,
                                Map.class,
                                Map.class, Set.class);
                m.setAccessible(true);

                String out = (String) m.invoke(
                                marketingAnalysisService,
                                report,
                                buildChartsDataSeasonalityOnly(),
                                buildPlaceholderMap(),
                                new HashSet<String>());

                int tocEnd = out.indexOf("## III.");
                assertTrue(tocEnd > 0, "Sanity check: section III should exist");
                String tocPart = out.substring(0, tocEnd);
                assertFalse(tocPart.contains("[[CHART_SEASONALITY]]"), "Must not insert into TOC block");

                int s3Start = out.indexOf("## III.");
                int s4Start = out.indexOf("## IV.");
                assertTrue(s3Start >= 0 && s4Start > s3Start, "Sanity check: III before IV");
                String section3Body = out.substring(s3Start, s4Start);
                assertTrue(section3Body.contains("[[CHART_SEASONALITY]]"),
                                "Must insert into section III body, not TOC");
        }

        @Test
        void addMissingChartsToSections_whenAnyPlaceholderProcessed_shouldNotAppendAnything() throws Exception {
                String report = ""
                                + "## III. Анализ рынка и сезонности\n"
                                + "Тут есть текст про рынок.\n"
                                + "\n"
                                + "## IV. Анализ целевой аудитории\n"
                                + "Тут аудитория.\n";

                Method m = MarketingAnalysisService.class.getDeclaredMethod("addMissingChartsToSections", String.class,
                                Map.class,
                                Map.class, Set.class);
                m.setAccessible(true);

                // Simulate: placeholder was already successfully replaced earlier in the
                // pipeline
                // (replaceChartPlaceholders adds normalized tokens to processedPlaceholders).
                Set<String> processed = new HashSet<>();
                processed.add("[[CHART_SEASONALITY]]".replaceAll("\\s+", "").toUpperCase(java.util.Locale.ROOT));

                String out = (String) m.invoke(
                                marketingAnalysisService,
                                report,
                                buildChartsDataSeasonalityOnly(),
                                buildPlaceholderMap(),
                                processed);

                assertEquals(report, out, "If any placeholder was processed, must not append any missing placeholders");
        }

        @Test
        void addMissingChartsToSections_shouldInsertChannelsAndMatrixIntoSectionVII() throws Exception {
                String report = ""
                                + "## Оглавление\n"
                                + "- [VII. Каналы продвижения и их потенциал](#section-vii)\n"
                                + "\n"
                                + "## VII. Каналы продвижения и их потенциал\n"
                                + "Тут есть текст про каналы.\n"
                                + "\n"
                                + "## VIII. Воронка спроса\n"
                                + "Тут есть текст про воронку.\n";

                Method m = MarketingAnalysisService.class.getDeclaredMethod("addMissingChartsToSections", String.class,
                                Map.class,
                                Map.class, Set.class);
                m.setAccessible(true);

                String out = (String) m.invoke(
                                marketingAnalysisService,
                                report,
                                buildChartsDataChannelsAndMatrix(),
                                buildPlaceholderMap(),
                                new HashSet<String>());

                int s7Start = out.indexOf("## VII.");
                int s8Start = out.indexOf("## VIII.");
                assertTrue(s7Start >= 0 && s8Start > s7Start, "Sanity check: VII before VIII");
                String section7Body = out.substring(s7Start, s8Start);
                assertTrue(section7Body.contains("[[CHART_CHANNELS]]"), "Must insert CHART_CHANNELS into section VII");
                assertTrue(section7Body.contains("[[CHART_CHANNEL_MATRIX]]"),
                                "Must insert CHART_CHANNEL_MATRIX into section VII");
        }

        @Test
        void buildChartJsonBlock_shouldStartWithJsonKey_andEndWithDoubleNewline() throws Exception {
                Method m = MarketingAnalysisService.class.getDeclaredMethod("buildChartJsonBlock", String.class,
                                String.class);
                m.setAccessible(true);

                String out = (String) m.invoke(marketingAnalysisService, "segmentsChannelMatrix", "{\"a\":1}");

                assertTrue(out.startsWith("```json:segmentsChannelMatrix\n"),
                                "Block must start strictly with ```json:<key> and newline");
                assertTrue(out.endsWith("\n```\n\n"), "Block must end with closing fence followed by two newlines");

                // Ensure there is no whitespace before opening fence (strict parsing on
                // frontend)
                assertTrue(Pattern.compile("(?m)^```json:segmentsChannelMatrix$").matcher(out).find(),
                                "Opening fence line must be exactly ```json:<key> with no leading spaces");
        }

        @Test
        void replaceChartPlaceholders_shouldRemoveGenericChartDotsPlaceholder() throws Exception {
                Method m = MarketingAnalysisService.class.getDeclaredMethod("replaceChartPlaceholders", String.class,
                                Map.class);
                m.setAccessible(true);

                String report = ""
                                + "## I. Краткое резюме\n"
                                + "Текст перед.\n"
                                + "[[CHART_...]]\n"
                                + "Текст после.\n";

                // Even if chartsData is empty, generic placeholders must be removed.
                String out = (String) m.invoke(marketingAnalysisService, report, new HashMap<String, Object>());

                assertFalse(out.contains("[[CHART_...]]"),
                                "Generic placeholder [[CHART_...]] must be removed from fullAnalysis");
        }
}
