package at.procon.dip.embedding.service;

import at.procon.dip.domain.document.entity.Document;
import at.procon.dip.embedding.config.EmbeddingPolicyCondition;
import at.procon.dip.embedding.config.EmbeddingPolicyProperties;
import at.procon.dip.embedding.config.EmbeddingPolicyRule;
import at.procon.dip.embedding.config.EmbeddingPolicyUse;
import at.procon.dip.embedding.policy.EmbeddingPolicy;
import at.procon.dip.ingestion.spi.SourceDescriptor;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class DefaultEmbeddingPolicyResolver implements EmbeddingPolicyResolver {

    private final EmbeddingPolicyProperties properties;

    @Override
    public EmbeddingPolicy resolve(Document document, SourceDescriptor sourceDescriptor) {
        String overridePolicy = attributeValue(sourceDescriptor, "embeddingPolicyKey");
        if (overridePolicy != null) {
            return policyByKey(overridePolicy);
        }

        String policyHint = policyHint(sourceDescriptor);
        if (policyHint != null) {
            return policyByKey(policyHint);
        }

        for (EmbeddingPolicyRule rule : properties.getRules()) {
            if (matches(rule.getWhen(), document, sourceDescriptor)) {
                return toPolicy(rule.getUse());
            }
        }

        return toPolicy(properties.getDefaultPolicy());
    }

    private EmbeddingPolicy policyByKey(String policyKey) {
        for (EmbeddingPolicyRule rule : properties.getRules()) {
            if (rule.getUse() != null && policyKey.equals(rule.getUse().getPolicyKey())) {
                return toPolicy(rule.getUse());
            }
        }
        EmbeddingPolicyUse def = properties.getDefaultPolicy();
        if (def != null && policyKey.equals(def.getPolicyKey())) {
            return toPolicy(def);
        }
        throw new IllegalArgumentException("Unknown embedding policy key: " + policyKey);
    }

    private EmbeddingPolicy toPolicy(EmbeddingPolicyUse use) {
        if (use == null) {
            throw new IllegalStateException("Embedding policy configuration is missing");
        }
        return new EmbeddingPolicy(
                use.getPolicyKey(),
                use.getModelKey(),
                use.getQueryModelKey(),
                use.getProfileKey(),
                use.isEnabled()
        );
    }

    private boolean matches(EmbeddingPolicyCondition c, Document document, SourceDescriptor sourceDescriptor) {
        if (c == null) {
            return true;
        }

        if (!matchesExact(c.getDocumentType(), enumName(document != null ? document.getDocumentType() : null))) {
            return false;
        }
        if (!matchesExact(c.getDocumentFamily(), enumName(document != null ? document.getDocumentFamily() : null))) {
            return false;
        }
        if (!matchesExact(c.getSourceType(), enumName(sourceDescriptor != null ? sourceDescriptor.sourceType() : null))) {
            return false;
        }
        if (!matchesMime(c.getMimeType(), sourceDescriptor != null ? sourceDescriptor.mediaType() : null)) {
            return false;
        }
        if (!matchesExact(c.getLanguage(), document != null ? document.getLanguageCode() : null)) {
            return false;
        }
        if (!matchesExact(c.getOwnerTenantKey(), document != null && document.getOwnerTenant() != null ? document.getOwnerTenant().getTenantKey() : null )) {
            return false;
        }
        return matchesExact(c.getEmbeddingPolicyHint(), policyHint(sourceDescriptor));
    }

    private boolean matchesExact(String expected, String actual) {
        if (expected == null || expected.isBlank()) {
            return true;
        }
        return Objects.equals(expected, actual);
    }

    private boolean matchesMime(String pattern, String actual) {
        if (pattern == null || pattern.isBlank()) {
            return true;
        }
        if (actual == null || actual.isBlank()) {
            return false;
        }
        return Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).matcher(actual).matches();
    }

    private String enumName(Enum<?> value) {
        return value != null ? value.name() : null;
    }

    private String policyHint(SourceDescriptor sourceDescriptor) {
        return attributeValue(sourceDescriptor, "embeddingPolicyHint");
    }

    private String attributeValue(SourceDescriptor sourceDescriptor, String key) {
        if (sourceDescriptor == null) {
            return null;
        }
        Map<String, String> attributes = sourceDescriptor.attributes();
        if (attributes == null) {
            return null;
        }
        String value = attributes.get(key);
        return (value == null || value.isBlank()) ? null : value;
    }
}
