package click.kamil.springstatemachineexporter.html;

import click.kamil.springstatemachineexporter.analysis.enricher.CallChainEnricher;
import click.kamil.springstatemachineexporter.analysis.enricher.EntryPointEnricher;
import click.kamil.springstatemachineexporter.analysis.enricher.PropertyEnricher;
import click.kamil.springstatemachineexporter.analysis.enricher.TriggerEnricher;
import click.kamil.springstatemachineexporter.analysis.model.AnalysisResult;
import click.kamil.springstatemachineexporter.analysis.model.BusinessFlow;
import click.kamil.springstatemachineexporter.analysis.model.FlowStep;
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
import click.kamil.springstatemachineexporter.analysis.model.LinkResolution;
import click.kamil.springstatemachineexporter.analysis.service.EnrichmentService;
import click.kamil.springstatemachineexporter.ast.app.StateMachineAggregator;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import click.kamil.springstatemachineexporter.exporter.ExportOptions;
import click.kamil.springstatemachineexporter.html.exporter.HtmlExporter;
import click.kamil.springstatemachineexporter.model.Transition;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class HtmlExporterTest {

    @Test
    void shouldGenerateCompleteInteractivePortal() throws IOException {
        // 1. Setup paths
        Path projectRoot = Path.of("..").toAbsolutePath().normalize();
        Path inputDir = projectRoot.resolve("state_machines/ultimate_ecosystem_sm");
        
        // 2. Perform Analysis
        CodebaseContext context = new CodebaseContext();
        context.setSourcepath(List.of(inputDir.toString()));
        context.setResolveBindings(true);
        context.scan(inputDir);

        TypeDeclaration td = context.getTypeDeclarations().stream()
                .filter(t -> context.getFqn(t).equals("click.kamil.ultimate.config.UltimateStateMachineConfig"))
                .findFirst()
                .orElseThrow();

        StateMachineAggregator aggregator = new StateMachineAggregator(context);
        List<Transition> transitions = aggregator.aggregateTransitions(td);
        aggregator.aggregateStates(td);
        
        // 3. Documented Flows
        List<BusinessFlow> flows = List.of(
            BusinessFlow.builder().name("Test Flow").description("Desc").steps(List.of(FlowStep.ofEvent("CREATE"))).build()
        );

        AnalysisResult result = AnalysisResult.builder()
                .name("UltimatePortal")
                .transitions(transitions)
                .startStates(aggregator.getInitialStates())
                .endStates(aggregator.getEndStates())
                .flows(flows)
                .build();

        EnrichmentService enrichmentService = new EnrichmentService(List.of(
                new TriggerEnricher(),
                new EntryPointEnricher(),
                new PropertyEnricher(),
                new CallChainEnricher()
        ));
        enrichmentService.enrich(result, context, new click.kamil.springstatemachineexporter.analysis.service.JdtIntelligenceProvider(context, inputDir));

        // 4. Export
        HtmlExporter exporter = new HtmlExporter();
        String html = exporter.export(result, ExportOptions.builder().build());

        // 5. Assertions - Structural Integrity
        assertThat(html).contains("<!DOCTYPE html>");
        assertThat(html).contains("UltimatePortal");
        assertThat(html).contains("Test Flow"); // Flows tab check
        
        // 6. Assertions - Data Payloads
        assertThat(html).contains("TicketRequest"); // @RequestBody extraction
        assertThat(html).contains("ticketId"); // JMS param extraction
        assertThat(html).contains("ticket.close.queue"); // Destination extraction

        // 7. Assertions - Surgical SVG Identification
        // Every event should be wrapped in an identifying hyperlink for robust JS selection
        
        assertThat(html).contains("__CREATE");
        assertThat(html).contains("__ASSIGN");
        assertThat(html).contains("__CLOSE_TICKET");
    }

    @Test
    void shouldRenderSidebarWhenMatchedTransitionsExist() throws IOException {
        click.kamil.springstatemachineexporter.analysis.model.EntryPoint ep = click.kamil.springstatemachineexporter.analysis.model.EntryPoint.builder().name("Test").build();
        click.kamil.springstatemachineexporter.analysis.model.CallChain chain = click.kamil.springstatemachineexporter.analysis.model.CallChain.builder()
                .matchedTransitions(List.of(click.kamil.springstatemachineexporter.analysis.model.MatchedTransition.builder().event("TEST_EVENT").build()))
                .build();
        click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata metadata = click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata.builder()
                .entryPoints(List.of(ep))
                .callChains(List.of(chain))
                .build();
        AnalysisResult result = AnalysisResult.builder()
                .name("SidebarTest")
                .transitions(List.of())
                .startStates(java.util.Collections.emptySet())
                .endStates(java.util.Collections.emptySet())
                .metadata(metadata)
                .build();

        HtmlExporter exporter = new HtmlExporter();
        String html = exporter.export(result, ExportOptions.builder().includeMetadataPane(true).build());

        assertThat(html).contains("id=\"sidebar\"");
        assertThat(html).contains("id=\"ep-list\"");
    }

    @Test
    void shouldRenderSidebarWhenUnresolvedExternalChainExists() throws IOException {
        EntryPoint ep = EntryPoint.builder()
                .type(EntryPoint.Type.REST)
                .name("POST /api/transition/{event}")
                .className("com.example.web.DispatcherController")
                .methodName("dispatch")
                .build();
        CallChain chain = CallChain.builder()
                .entryPoint(ep)
                .linkResolution(LinkResolution.UNRESOLVED_EXTERNAL)
                .triggerPoint(click.kamil.springstatemachineexporter.analysis.model.TriggerPoint.builder()
                        .event("OrderEvent.valueOf(eventString.toUpperCase())")
                        .external(true)
                        .build())
                .build();
        click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata metadata = click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata.builder()
                .entryPoints(List.of(ep))
                .callChains(List.of(chain))
                .build();
        AnalysisResult result = AnalysisResult.builder()
                .name("SidebarUnresolvedTest")
                .transitions(List.of())
                .startStates(java.util.Collections.emptySet())
                .endStates(java.util.Collections.emptySet())
                .metadata(metadata)
                .build();

        HtmlExporter exporter = new HtmlExporter();
        String html = exporter.export(result, ExportOptions.builder().includeMetadataPane(true).build());

        assertThat(html).contains("id=\"sidebar\"");
        assertThat(html).contains("id=\"ep-list\"");
    }

    @Test
    void shouldNotRenderSidebarWhenNoMatchedTransitionsExist() throws IOException {
        click.kamil.springstatemachineexporter.analysis.model.EntryPoint ep = click.kamil.springstatemachineexporter.analysis.model.EntryPoint.builder().name("Test").build();
        CallChain chain = CallChain.builder()
                .triggerPoint(click.kamil.springstatemachineexporter.analysis.model.TriggerPoint.builder().event("TEST_EVENT").build())
                .linkResolution(LinkResolution.NO_MATCH)
                .matchedTransitions(List.of()) // EMPTY
                .build();
        click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata metadata = click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata.builder()
                .entryPoints(List.of(ep))
                .callChains(List.of(chain))
                .build();
        
        Transition transition = new Transition();
        transition.setEvent(click.kamil.springstatemachineexporter.model.Event.of("TEST_EVENT"));
        transition.setSourceStates(List.of());
        transition.setTargetStates(List.of());
        
        AnalysisResult result = AnalysisResult.builder()
                .name("SidebarTestEmpty")
                .transitions(List.of(transition))
                .startStates(java.util.Collections.emptySet())
                .endStates(java.util.Collections.emptySet())
                .metadata(metadata)
                .build();

        HtmlExporter exporter = new HtmlExporter();
        String html = exporter.export(result, ExportOptions.builder().includeMetadataPane(true).build());

        assertThat(html).doesNotContain("id=\"sidebar\"");
        assertThat(html).doesNotContain("id=\"ep-list\"");
    }

    @Test
    void shouldContainCorrectJsLogicForEventTooltipResolution() throws IOException {
        HtmlExporter exporter = new HtmlExporter();
        AnalysisResult result = AnalysisResult.builder()
                .name("JsTest")
                .transitions(List.of())
                .startStates(java.util.Collections.emptySet())
                .endStates(java.util.Collections.emptySet())
                .build();
        
        String html = exporter.export(result, ExportOptions.builder().build());

        assertThat(html).contains("function formatForLinkId(identifier, format)");
        assertThat(html).contains("const linkEventFormat = 'fn'");
        assertThat(html).contains("function buildLinkKey(source, event)");
    }
}
