package org.javalite.db_migrator;

import org.apache.maven.project.MavenProject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MigrationResolver {

    private static final Logger LOGGER = LoggerFactory.getLogger(MigrationResolver.class);
    private final String migrationsLocation;
    private MavenProject mavenProject;

    private Properties mergeProperties;

    private static final Pattern MIGRATION_FILE_PATTERN = Pattern.compile("^(\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d)_.*\\.(sql|groovy)");

    public MigrationResolver(MavenProject mavenProject, String migrationsLocation) {
        this(mavenProject, migrationsLocation, null);
    }

    public MigrationResolver(MavenProject mavenProject, String migrationsLocation, Properties mergeProperties) {
        this.mavenProject = mavenProject;
        this.migrationsLocation = migrationsLocation;
        this.mergeProperties = mergeProperties;
    }

    public List<Migration> resolve() {
        File location = new File(migrationsLocation);
        LOGGER.info("Trying migrations at: {} ", location.getAbsolutePath());

        //assume flat directory of migrations
        File[] files = location.listFiles();

        if(files == null || files.length == 0) throw  new MigrationException("No migrations are found at: " + location.getAbsolutePath());

        //filter out garbage
        List<File> migrationsFiles = new ArrayList<File>();
        for (File file : files) {
            if (!file.isDirectory() && MIGRATION_FILE_PATTERN.matcher(file.getName()).matches()) {
                migrationsFiles.add(file);
            }
        }
        checkDuplicateVersions(migrationsFiles);


        List<Migration> migrations = new ArrayList<>();

        // Extract versions and create executable migrations for each resource.
        for (File migrationFile: migrationsFiles) {
            String version = extractVersion(migrationFile.getName());

            if(migrationFile.getName().endsWith("sql")){
                migrations.add(new SQLMigration(version, migrationFile, mergeProperties));
            }else if(migrationFile.getName().endsWith("groovy")){
                migrations.add(new GroovyMigration(mavenProject, version, migrationFile, mergeProperties));
            }else {
                throw new RuntimeException("file type not supported");
            }
        }

        Collections.sort(migrations);
        return migrations;
    }

    private List<String> extractVersions(List<File> migrationsFiles) {
        List<String> versions = new ArrayList<>();
        for (File file : migrationsFiles) {
            versions.add(extractVersion(file.getName()));
        }
        return versions;
    }


    public void checkDuplicateVersions(List<File> files){
        List<String> versions = extractVersions(files);
        Set<String> versionsHash =  new HashSet<String>();
        for (String version: versions) {
            boolean isNew = versionsHash.add(version);
            if(!isNew) throw new MigrationException("Duplicate version discovered: " + version);
        }
    }


    public String extractVersion(String name) {
        String errorMessage = "Error parsing migration version from " + name;
        try {
            Matcher matcher = MIGRATION_FILE_PATTERN.matcher(name);
            boolean found = matcher.find();
            if(!found) throw  new MigrationException(errorMessage);
            return matcher.group(1);
        }catch(MigrationException e){
            throw e;
        }
        catch (Exception e) {
            throw new MigrationException(errorMessage, e);
        }
    }
}
