/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2022 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 org.jboss.installer.validators;

import org.jboss.installer.core.FileUtils;
import org.jboss.installer.core.LanguageUtils;
import org.jboss.installer.core.ValidationResult;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import static org.jboss.installer.validators.FileValidator.NOT_EXISTS_ERROR;

public class MavenOfflineRepositoryValidator extends MavenRepositoryValidator {

    public static final String EMPTY_ERROR = "target_screen.maven_repository_empty_error";
    public static final String NEITHER_DIRECTORY_NOR_ZIPFILE_ERROR = "target_screen.neither_directory_not_zipfile_error";
    public static final String INVALID_FILE_URI_ERROR = "target_screen.invalid.file.uri.error";
    public static final String MALFORMED_URL_ERROR = "target_screen.malformed.url.error";

    public static final String MULTIPLE_REPO_ROOTS = "target_screen.invalid.zip.multiple_roots.error";
    public static final String NO_REPO_ROOTS = "target_screen.invalid.zip.no_roots.error";
    public static final String INVALID_ZIP = "target_screen.invalid.zip.error";
    private static final String URI_FILE_PREFIX = "file:";

    public MavenOfflineRepositoryValidator(LanguageUtils langUtils) {
        super(langUtils);
    }

    public ValidationResult validate(String repositories) {
        if (repositories == null || repositories.isEmpty()) {
            return ValidationResult.error(getLanguageUtils().getString(EMPTY_ERROR));
        }

        for (String repo: Arrays.asList(repositories.trim().split(","))) {
            String repository = repo.trim();
            if (isRemoteProtocol(repository)) {
                // it is a URL
                try {
                    new URI(repository).toURL();
                } catch (MalformedURLException | URISyntaxException e) {
                    return ValidationResult.error(getLanguageUtils().getString(MALFORMED_URL_ERROR, e.getMessage()));
                }
            } else {
                // it is a directory or a file
                try {
                    final Path path;
                    try {
                        path = isFileUri(repository) ? Path.of(new URI(repository)).toAbsolutePath() : Path.of(repository).toAbsolutePath();
                    } catch (InvalidPathException e) {
                        return PathValidator.newInstance("generic", getLanguageUtils()).validate(repository);
                    }

                    if (!Files.exists(path)) {
                        return ValidationResult.error(getLanguageUtils().getString(NOT_EXISTS_ERROR, path.toString()));
                    }
                    // then it has to be either a directory or a ZIP file, nothing else
                    if (!Files.isDirectory(path) && !FileUtils.isZipFile(path.toFile())) {
                        return ValidationResult.error(getLanguageUtils().getString(NEITHER_DIRECTORY_NOR_ZIPFILE_ERROR, repository.toString()));
                    }
                    if (FileUtils.isZipFile(path.toFile())) {
                        String repoRoot = null;
                        try (ZipFile zipFile = new ZipFile(path.toFile())) {
                            final Pattern repoRootPattern = Pattern.compile(".*/maven-repository/");
                            Enumeration<? extends ZipEntry> entries = zipFile.entries();
                            while (entries.hasMoreElements()) {
                                final ZipEntry entry = entries.nextElement();
                                if (entry.isDirectory() && repoRootPattern.matcher(entry.getName()).matches()) {
                                    if (repoRoot == null) {
                                        repoRoot = entry.getName();
                                    } else {
                                        return ValidationResult.error(getLanguageUtils().getString(MULTIPLE_REPO_ROOTS,
                                                entry.getName().toString(), repoRoot.toString()));
                                    }
                                }
                            }
                        } catch (IOException e) {
                            return ValidationResult.error(getLanguageUtils().getString(INVALID_ZIP, e.getMessage()));
                        }
                        if (repoRoot == null) {
                            return ValidationResult.error(getLanguageUtils().getString(NO_REPO_ROOTS));
                        }
                    }
                } catch (URISyntaxException e) {
                    return ValidationResult.error(getLanguageUtils().getString(INVALID_FILE_URI_ERROR, e.getMessage()));
                }
            }
        }

        return ValidationResult.ok();
    }

    public static boolean isFileUri(String uri) {
        return uri.startsWith(URI_FILE_PREFIX);
    }
}
