/**
 * Copyright (c) 2018-2019, Sylvain Baudoin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.sbaudoin.sonar.plugins.ansible.rules;

import com.github.sbaudoin.sonar.plugins.ansible.checks.AnsibleCheckRepository;
import com.github.sbaudoin.sonar.plugins.ansible.util.FileSystem;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import com.github.sbaudoin.sonar.plugins.yaml.languages.YamlLanguage;
import org.sonarsource.analyzer.commons.RuleMetadataLoader;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

public abstract class AbstractAnsibleRulesDefinition implements RulesDefinition {
    private static final Logger LOGGER = Loggers.get(AbstractAnsibleRulesDefinition.class);


    @Override
    public void define(RulesDefinition.Context context) {
        RulesDefinition.NewRepository repository = context.createRepository(AnsibleCheckRepository.REPOSITORY_KEY, YamlLanguage.KEY).setName(AnsibleCheckRepository.REPOSITORY_NAME);

        RuleMetadataLoader metadataLoader = new RuleMetadataLoader(getRuleDefinitionPath());

        List<String> keys = new ArrayList<>();
        getRuleKeys().forEach(keys::add);
        metadataLoader.addRulesByRuleKey(repository, keys);

        repository.done();
    }


    protected abstract String getRuleDefinitionPath();


    private List<String> getRuleKeys() {
        List<String> keys = new ArrayList();

        URL definitionDir = getClass().getClassLoader().getResource(getRuleDefinitionPath());
        if (definitionDir == null) {
            LOGGER.info("No Ansible Lint rules found");
            return keys;
        }
        LOGGER.debug("Creating FileSystem for " + definitionDir);
        try (FileSystem fs = new FileSystem(definitionDir.toURI())) {
            LOGGER.debug("Reading rule definition files...");
            fs.readDirectory(definitionDir.toURI()).filter(entry -> entry.toString().endsWith(".json")).forEach(entry -> {
                String key = getRuleKey(entry);
                LOGGER.debug("RuleKey of {} is {}", entry.toString(), key);
                if (htmlDescFileExists(entry)) {
                    keys.add(key);
                } else {
                    LOGGER.warn("Rule {} defined but not described (.html file missing)", key);
                }
            });
        } catch (URISyntaxException e) {
            LOGGER.error("Cannot find additional ansible-lint rules", e);
        } catch (IOException e) {
            LOGGER.error("Unknown error", e);
        }

        return keys;
    }

    private String getRuleKey(Path definitionFile) {
        return definitionFile.getFileName().toString().replace(".json", "");
    }

    private boolean htmlDescFileExists(Path definitionFile) {
        return Files.exists(definitionFile.resolveSibling(getRuleKey(definitionFile) + ".html"));
    }
}