/*
 * Copyright OpenSearch Contributors
 * SPDX-License-Identifier: Apache-2.0
 */

package org.opensearch.indexmanagement.indexstatemanagement

import org.apache.lucene.util.automaton.Operations
import org.opensearch.OpenSearchException
import org.opensearch.common.Strings
import org.opensearch.common.ValidationException
import org.opensearch.common.regex.Regex
import org.opensearch.indexmanagement.indexstatemanagement.model.ISMTemplate
import org.opensearch.indexmanagement.util.IndexManagementException

/**
 * validate the template Name and indexPattern provided in the template
 *
 * get the idea from ES validate function in MetadataIndexTemplateService
 * acknowledge https://github.com/a2lin who should be the first contributor
 */
@Suppress("ComplexMethod")
fun validateFormat(indexPatterns: List<String>): OpenSearchException? {
    val indexPatternFormatErrors = mutableListOf<String>()
    for (indexPattern in indexPatterns) {
        if (indexPattern.contains("#")) {
            indexPatternFormatErrors.add("index_pattern [$indexPattern] must not contain a '#'")
        }
        if (indexPattern.contains(":")) {
            indexPatternFormatErrors.add("index_pattern [$indexPattern] must not contain a ':'")
        }
        if (indexPattern.startsWith("_")) {
            indexPatternFormatErrors.add("index_pattern [$indexPattern] must not start with '_'")
        }
        if (!Strings.validFileNameExcludingAstrix(indexPattern)) {
            indexPatternFormatErrors.add(
                "index_pattern [" + indexPattern + "] must not contain the following characters " +
                    Strings.INVALID_FILENAME_CHARS
            )
        }
    }

    if (indexPatternFormatErrors.size > 0) {
        val validationException = ValidationException()
        validationException.addValidationErrors(indexPatternFormatErrors)
        return IndexManagementException.wrap(validationException)
    }
    return null
}

fun List<ISMTemplate>.findSelfConflictingTemplates(): Pair<List<String>, List<String>>? {
    val priorityToTemplates = mutableMapOf<Int, List<ISMTemplate>>()
    this.forEach {
        val templateList = priorityToTemplates[it.priority]
        if (templateList != null) {
            priorityToTemplates[it.priority] = templateList.plus(it)
        } else {
            priorityToTemplates[it.priority] = mutableListOf(it)
        }
    }
    priorityToTemplates.forEach { (_, templateList) ->
        // same priority
        val indexPatternsList = templateList.map { it.indexPatterns }
        if (indexPatternsList.size > 1) {
            indexPatternsList.forEachIndexed { ind, indexPatterns ->
                val comparePatterns = indexPatternsList.subList(ind + 1, indexPatternsList.size).flatten()
                if (overlapping(indexPatterns, comparePatterns)) {
                    return indexPatterns to comparePatterns
                }
            }
        }
    }

    return null
}

@Suppress("SpreadOperator")
fun overlapping(p1: List<String>, p2: List<String>): Boolean {
    if (p1.isEmpty() || p2.isEmpty()) return false
    val a1 = Regex.simpleMatchToAutomaton(*p1.toTypedArray())
    val a2 = Regex.simpleMatchToAutomaton(*p2.toTypedArray())
    return !Operations.isEmpty(Operations.intersection(a1, a2))
}

/**
 * find policy templates whose index patterns overlap with given template
 *
 * @return map of overlapping template name to its index patterns
 */
fun Map<String, List<ISMTemplate>>.findConflictingPolicyTemplates(
    candidate: String,
    indexPatterns: List<String>,
    priority: Int
): Map<String, List<String>> {
    val overlappingTemplates = mutableMapOf<String, List<String>>()

    this.forEach { (policyID, templateList) ->
        templateList.filter { it.priority == priority }
            .map { it.indexPatterns }
            .forEach {
                if (overlapping(indexPatterns, it)) {
                    overlappingTemplates[policyID] = it
                }
            }
    }
    overlappingTemplates.remove(candidate)
    return overlappingTemplates
}