/**
 * Copyright (c) 2022 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.plugin.jbiplugin;

import java.util.ArrayList;
import java.util.List;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;

import com.ebmwebsourcing.easycommons.lang.UncheckedException;

/**
 * Validate a JBI Archive of a project.
 * 
 * @author Christophe DENEUX - Linagora
 */
@Mojo(name = "jbi-validate", requiresDependencyResolution = ResolutionScope.RUNTIME)
public class JBIValidateMojo extends JBIAbstractConfigurableMojo {

    /**
     * The JBI component to use to run the service unit. Required by service unit project.
     */
    @Parameter(required = false, defaultValue = "")
    protected String jbiComponentUsed;

    /**
     * Boolean which active the update of the JBI descriptor fields against the Maven parameters
     */
    @Parameter(required = true, defaultValue = "true")
    protected boolean updateJBIXml = true;

    @Override
    public void executeMojo() throws MojoExecutionException, MojoFailureException {

        switch (this.project.getPackaging()) {
            case PACKAGING_COMPONENT:
                this.validateComponent();
                break;
            case PACKAGING_SA:
                this.validateServiceAssembly();
                break;
            case PACKAGING_SU:
                this.validateServiceUnit();
                break;
            case PACKAGING_SL:
                this.validateSharedLibrary();
                break;
            default: throw new UncheckedException("Impossible case: "+ project.getPackaging());
        }
    }

    /**
     * Validate a JBI component project
     */
    private void validateComponent() throws MojoExecutionException {

        this.info("Start validating the JBI component project " + this.projectArtifact.getArtifactId());

        // Nothing to do to validate a JBI component project

        this.info("JBI component project validated.");
    }

    /**
     * Validate a JBI service-unit project.
     */
    private void validateServiceUnit() throws MojoFailureException {

        this.info("Start validating JBI service-unit project " + this.projectArtifact.getArtifactId());
        this.checkSuAgainstJbiComponent(this.project,
                // Note: parameter 'updateJBIXml' must be set to 'true' to be able to write the target component as
                // extension in the service unit JBI descriptor. That's why we can consider the JBI component to use as
                // not set if parameter 'updateJBIXml' is set to 'false'.
                this.updateJBIXml ? this.jbiComponentUsed : null);
        this.info("JBI service-unit project validated.");
    }

    private void checkSuAgainstJbiComponent(final MavenProject serviceUnitProject,
            final String jbiComponentToUsed) throws MojoFailureException {

        final List<Dependency> serviceUnitDependencies = serviceUnitProject.getDependencies();
        final List<Dependency> jbiComponentDependencies = new ArrayList<Dependency>();
        for (final Dependency serviceUnitDependency : serviceUnitDependencies) {
            this.debug("SU dependency: " + serviceUnitDependency);
            if (PACKAGING_COMPONENT.equals(serviceUnitDependency.getType())) {
                // We have found a component
                this.debug("Component dependency found: " + serviceUnitDependency);
                jbiComponentDependencies.add(serviceUnitDependency);
            }
        }

        if (jbiComponentDependencies.isEmpty()) {
            final String msg = String.format("A dependency as JBI component is required for a JBI service unit '%s'.",
                    serviceUnitProject.getArtifactId());
            this.error(msg);
            throw new MojoFailureException(msg);
        } else if (jbiComponentDependencies.size() > 1) {
            this.debug(
                    "More than one JBI component dependency declared. The JBI component to use must be configured.");

            if (jbiComponentToUsed != null && !jbiComponentToUsed.trim().isEmpty()) {
                boolean jbiComponentUsedFound = false;
                for (final Dependency jbiComponentDependency : jbiComponentDependencies) {
                    assert PACKAGING_COMPONENT.equals(jbiComponentDependency.getType());
                    
                    final String jbiComponentDependencyGA = jbiComponentDependency.getGroupId() + ":" + jbiComponentDependency.getArtifactId();
                    if (jbiComponentDependencyGA.equals(jbiComponentToUsed)) {
                        jbiComponentUsedFound = true;
                    }
                }
                if (!jbiComponentUsedFound) {
                    final String msg = String.format(
                            "The JBI component configured as used '%s' is not found as dependency.",
                            jbiComponentToUsed);
                    this.error(msg);
                    throw new MojoFailureException(msg);
                }
            } else {
                final String msg = String.format(
                        "Several JBI component dependencies are declared for the JBI service unit '%s'. You must use parameters 'jbiComponentsUsed' and 'updateJBIXml' to set the right JBI component to use to run this service unit.",
                        serviceUnitProject.getArtifactId());
                this.error(msg);
                throw new MojoFailureException(msg);
            }
        }

    }

    /**
     * Validate a JBI service-assembly project.
     * 
     * @throws MojoFailureException
     */
    private void validateServiceAssembly() throws MojoExecutionException, MojoFailureException {

        this.info("Start validating the JBI service-assembly project " + this.projectArtifact.getArtifactId());

        // Check that each service unit dependency depends on only one JBI component
        final List<Dependency> dependencies = project.getDependencies();
        for (final Dependency dependency : dependencies) {

            if (PACKAGING_SU.equals(dependency.getType())) {
                try {
                    final Artifact serviceUnitArtifact = this.createDependencyArtifact(dependency);

                    this.artifactResolver.resolve(serviceUnitArtifact, this.project.getRemoteArtifactRepositories(),
                            this.localRepository);

                    final MavenProject serviceUnitProject = this.mavenProjectBuilder.buildFromRepository(
                            serviceUnitArtifact, this.project.getRemoteArtifactRepositories(), this.localRepository);

                    this.checkSuAgainstJbiComponent(serviceUnitProject,
                            this.extractComponentToUseFromServiceUnitJbiDescr(serviceUnitArtifact));

                } catch (final MojoFailureException e) {
                    throw e;
                } catch (final Exception e) {
                    throw new MojoExecutionException(e.getLocalizedMessage(), e);
                }
            }
        }

        this.info("JBI service-assembly project validated.");
    }

    /**
     * Validate a JBI shared-library project.
     */
    private void validateSharedLibrary() throws MojoExecutionException {

        this.info("Start validating the JBI shared-library project " + this.projectArtifact.getArtifactId());

        // Nothing to do to validate a JBI shared-library project

        this.info("JBI shared-library project validated.");
    }

    @Override
    protected MavenProject getMavenProject() {
        return this.project;
    }
}
