/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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 org.jboss.installer.validators;

import org.jboss.installer.postinstall.task.JsfLibraryTask;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JsfUtils {

    public static String POM_PATH = "META-INF/maven/.*/pom\\.properties$";
    public static String MANIFEST_PATH = "META-INF/MANIFEST.MF$";

    /**
     * Returns the cleaned version if it is valid
     * Otherwise, return the empty string
     * @param ver
     * @return
     */
    public String cleanedVer(String ver){

        if (ver == null) {
            return null;
        }
        //versions of the form a.b, a.b.c, a.b* , a.b.c*
        //where * is the decorator
        Pattern pattern = Pattern.compile("(?<version>^\\s*(4)\\.(0|1|2)(\\.(\\d){1,2})?)(?<decorator>.*)");
        Matcher matcher = pattern.matcher(ver);
        Matcher m = matcher.find() ? matcher : null ;

        if(m != null){
            String dec = m.group("decorator").trim();
            String cleanedVer = m.group("version").trim();
            //reject if decorator starts with a digit or ends with a delimiter
            // or if version is too short to fit form a.b
            if(cleanedVer.length() > 2 && (dec.length() == 0 || dec.matches("^\\D.*\\w$"))){
                return cleanedVer;
            }
        }
        return null;
    }

    public boolean verifyIsApi(JarFile jarFile) throws IOException {
        return verifyMetadataPropertyContainsString(jarFile, "api");
    }

    public boolean verifyIsImpl(JarFile jarFile) throws IOException {
        return verifyMetadataPropertyContainsString(jarFile, "impl");
    }

    public JsfLibraryTask.Config.JsfProject getProject(JarFile jarFile) throws IOException {
        String artifactId = getPropertyFromJar(jarFile, POM_PATH, "artifactId");
        if (artifactId != null) {
            String lowerCaseArtifactId = artifactId.toLowerCase(Locale.ROOT);
            if (lowerCaseArtifactId.contains("myfaces-")) {
                return JsfLibraryTask.Config.JsfProject.MyFaces;
            }
            if (lowerCaseArtifactId.contains("jsf-") || lowerCaseArtifactId.contains("jakarta.faces")) {
                return JsfLibraryTask.Config.JsfProject.Mojarra;
            }
        }

        artifactId = getPropertyFromJar(jarFile, MANIFEST_PATH, "Implementation-Title");
        if (artifactId != null) {
            if (artifactId.toLowerCase(Locale.ROOT).contains("myfaces-")) {
                return JsfLibraryTask.Config.JsfProject.MyFaces;
            }
            if (artifactId.toLowerCase(Locale.ROOT).contains("mojarra")) {
                return JsfLibraryTask.Config.JsfProject.Mojarra;
            }
        }
        return null;
    }

    public String getVersion(JarFile jarFile) throws IOException {
        String version = getPropertyFromJar(jarFile, POM_PATH, "version");
        if (version == null || version.isEmpty()) {
            return getPropertyFromJar(jarFile, MANIFEST_PATH, "Bundle-Version");
        }
        return version;
    }

    private boolean verifyMetadataPropertyContainsString(JarFile jarFile, String expected) throws IOException {
        String artifactId = getPropertyFromJar(jarFile, POM_PATH, "artifactId");
        if (artifactId != null && artifactId.toLowerCase(Locale.ROOT).contains(expected)) {
            return true;
        }

        artifactId = getPropertyFromJar(jarFile, MANIFEST_PATH, "Bundle-Description");
        return artifactId != null && artifactId.toLowerCase(Locale.ROOT).contains(expected);
    }

    private String getPropertyFromJar(JarFile jar, String path, String attribute) throws IOException {

        JarEntry foundEntry = regexPath(jar, path);

        if (foundEntry != null) {
            Properties prop = new Properties();
            InputStream pom = jar.getInputStream(foundEntry);
            prop.load(pom);
            pom.close();
            String res = prop.getProperty(attribute);
            return res != null ? res.trim() : "";
        }
        return "";
    }

    private JarEntry regexPath(JarFile jar, String path){
        Pattern regexPath =  Pattern.compile(path);
        Enumeration<JarEntry> entries = jar.entries();

        while(entries.hasMoreElements()){
            JarEntry entry = entries.nextElement();
            Matcher match = regexPath.matcher(entry.getName());
            if(match.matches()){
                return entry;
            }
        }
        return null;
    }
}
