package click.kamil.springstatemachineexporter.analysis.resolver;

import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;


@Slf4j
public class PropertyResolver {

    private final List<String> activeProfiles = new ArrayList<>();

    public void setActiveProfiles(List<String> profiles) {
        this.activeProfiles.clear();
        if (profiles != null) {
            this.activeProfiles.addAll(profiles);
        }
    }

    public Map<String, String> resolveProperties(Path rootDir) {
        Map<String, String> allProperties = new HashMap<>();
        
        // 1. Load base properties
        loadMatching(rootDir, "application.properties", allProperties);
        loadMatching(rootDir, "application.yml", allProperties);
        loadMatching(rootDir, "application.yaml", allProperties);

        // 2. Load profile-specific properties (overriding base)
        for (String profile : activeProfiles) {
            loadMatching(rootDir, "application-" + profile + ".properties", allProperties);
            loadMatching(rootDir, "application-" + profile + ".yml", allProperties);
            loadMatching(rootDir, "application-" + profile + ".yaml", allProperties);
        }
        
        return allProperties;
    }

    public Map<String, Map<String, String>> resolveAllProperties(Set<Path> rootDirs) {
        Map<String, Map<String, String>> profiles = new HashMap<>();
        
        for (Path rootDir : rootDirs) {
            // 1. Load default profile
            Map<String, String> defaultProps = profiles.computeIfAbsent("default", k -> new HashMap<>());
            loadMatching(rootDir, "application.properties", defaultProps);
            loadMatching(rootDir, "application.yml", defaultProps);
            loadMatching(rootDir, "application.yaml", defaultProps);

            // 2. Discover and load all other profiles
            try (Stream<Path> paths = Files.walk(rootDir)) {
                paths.filter(Files::isRegularFile)
                     .filter(p -> {
                         String name = p.getFileName().toString();
                         return name.startsWith("application-") && 
                                (name.endsWith(".properties") || name.endsWith(".yml") || name.endsWith(".yaml"));
                     })
                     .forEach(p -> {
                         String name = p.getFileName().toString();
                         String profile = name.substring("application-".length(), name.lastIndexOf('.'));
                         Map<String, String> loaded = p.toString().endsWith(".properties") ? loadProperties(p) : loadYaml(p);
                         profiles.computeIfAbsent(profile, k -> new HashMap<>()).putAll(loaded);
                     });
            } catch (IOException e) {
                log.error("Failed to scan for profiles in {}", rootDir, e);
            }
        }
        
        return profiles;
    }

    private void loadMatching(Path rootDir, String fileName, Map<String, String> target) {
        try (Stream<Path> paths = Files.walk(rootDir)) {
            paths.filter(p -> Files.isRegularFile(p))
                 .filter(p -> p.getFileName().toString().equals(fileName))
                 .forEach(p -> {
                     if (p.toString().endsWith(".properties")) {
                         target.putAll(loadProperties(p));
                     } else {
                         target.putAll(loadYaml(p));
                     }
                 });
        } catch (IOException e) {
            log.error("Failed to scan for {} in {}", fileName, rootDir, e);
        }
    }

    private Map<String, String> loadProperties(Path path) {
        Map<String, String> props = new HashMap<>();
        Properties p = new Properties();
        try (InputStream is = Files.newInputStream(path)) {
            p.load(is);
            p.forEach((k, v) -> props.put(k.toString(), v.toString()));
        } catch (IOException e) {
            log.warn("Failed to load properties from {}", path);
        }
        return props;
    }

    private Map<String, String> loadYaml(Path path) {
        Map<String, String> props = new HashMap<>();
        try {
            com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(new com.fasterxml.jackson.dataformat.yaml.YAMLFactory());
            Map<String, Object> map = mapper.readValue(path.toFile(), new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
            flattenYaml("", map, props);
        } catch (IOException e) {
            log.warn("Failed to load YAML from {}", path);
        }
        return props;
    }

    @SuppressWarnings("unchecked")
    private void flattenYaml(String prefix, Map<String, Object> map, Map<String, String> result) {
        if (map == null) return;
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey();
            Object value = entry.getValue();
            if (value instanceof Map) {
                flattenYaml(key, (Map<String, Object>) value, result);
            } else if (value != null) {
                result.put(key, value.toString());
            }
        }
    }

    public String resolveValue(String placeholder, Map<String, String> properties) {
        return click.kamil.springstatemachineexporter.analysis.util.PlaceholderResolver.resolve(placeholder, properties);
    }
}
