package click.kamil.springstatemachineexporter.analysis.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import click.kamil.springstatemachineexporter.model.Transition;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.jackson.Jacksonized;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Getter
@Setter
@Builder(toBuilder = true)
@Jacksonized
@JsonIgnoreProperties(ignoreUnknown = true)
public class AnalysisResult {
    private String name;
    @Builder.Default
    private Set<click.kamil.springstatemachineexporter.model.State> states = java.util.Collections.emptySet();
    private List<Transition> transitions;
    private Set<String> startStates;
    private Set<String> endStates;
    private boolean renderChoicesAsDiamonds;

    @Builder.Default
    private List<BusinessFlow> flows = java.util.Collections.emptyList();

    @Builder.Default
    private CodebaseMetadata metadata = CodebaseMetadata.empty();

    public void applyResolution(Map<String, String> properties) {
        var resolver = new click.kamil.springstatemachineexporter.analysis.resolver.PropertyResolver();
        
        // 1. Resolve start/end states strings
        if (startStates != null) {
            this.startStates = startStates.stream()
                    .map(s -> resolver.resolveValue(s, properties))
                    .collect(java.util.stream.Collectors.toSet());
        }
        if (endStates != null) {
            this.endStates = endStates.stream()
                    .map(s -> resolver.resolveValue(s, properties))
                    .collect(java.util.stream.Collectors.toSet());
        }

        // 2. Resolve states (State record is immutable, so recreate)
        if (states != null) {
            this.states = states.stream()
                    .map(s -> click.kamil.springstatemachineexporter.model.State.of(s.rawName(), resolver.resolveValue(s.fullIdentifier(), properties)))
                    .collect(java.util.stream.Collectors.toSet());
        }

        // 3. Resolve transitions
        if (transitions != null) {
            for (var t : transitions) {
                if (t.getEvent() != null) {
                    t.setEvent(resolver.resolveValue(t.getEvent(), properties));
                }
                // Resolve states within transitions
                t.setSourceStates(t.getSourceStates().stream()
                        .map(s -> click.kamil.springstatemachineexporter.model.State.of(s.rawName(), resolver.resolveValue(s.fullIdentifier(), properties)))
                        .collect(java.util.stream.Collectors.toList()));
                t.setTargetStates(t.getTargetStates().stream()
                        .map(s -> click.kamil.springstatemachineexporter.model.State.of(s.rawName(), resolver.resolveValue(s.fullIdentifier(), properties)))
                        .collect(java.util.stream.Collectors.toList()));
            }
        }

        // 4. Resolve metadata (triggers, entry points)
        if (metadata != null) {
            if (metadata.getTriggers() != null) {
                for (var trigger : metadata.getTriggers()) {
                    if (trigger.getEvent() != null) {
                        trigger.setEvent(resolver.resolveValue(trigger.getEvent(), properties));
                    }
                }
            }
            if (metadata.getEntryPoints() != null) {
                for (var ep : metadata.getEntryPoints()) {
                    if (ep.getName() != null) {
                        ep.setName(resolver.resolveValue(ep.getName(), properties));
                    }
                    if (ep.getMetadata() != null) {
                        Map<String, String> resolvedMeta = new java.util.HashMap<>();
                        for (var entry : ep.getMetadata().entrySet()) {
                            resolvedMeta.put(entry.getKey(), resolver.resolveValue(entry.getValue(), properties));
                        }
                        ep.setMetadata(resolvedMeta);
                    }
                }
            }
        }
    }

    public void addMetadata(CodebaseMetadata newMetadata) {
        if (newMetadata == null) return;
        
        List<TriggerPoint> combinedTriggers = new java.util.ArrayList<>(this.metadata.getTriggers());
        if (newMetadata.getTriggers() != null) combinedTriggers.addAll(newMetadata.getTriggers());

        List<EntryPoint> combinedEntryPoints = new java.util.ArrayList<>(this.metadata.getEntryPoints());
        if (newMetadata.getEntryPoints() != null) combinedEntryPoints.addAll(newMetadata.getEntryPoints());

        List<CallChain> combinedCallChains = new java.util.ArrayList<>(this.metadata.getCallChains());
        if (newMetadata.getCallChains() != null) combinedCallChains.addAll(newMetadata.getCallChains());

        java.util.Map<String, java.util.Map<String, String>> combinedProperties = new java.util.HashMap<>(this.metadata.getProperties());
        if (newMetadata.getProperties() != null) combinedProperties.putAll(newMetadata.getProperties());

        this.metadata = CodebaseMetadata.builder()
                .triggers(Collections.unmodifiableList(combinedTriggers))
                .entryPoints(Collections.unmodifiableList(combinedEntryPoints))
                .callChains(Collections.unmodifiableList(combinedCallChains))
                .properties(Collections.unmodifiableMap(combinedProperties))
                .build();
    }
}
