/*******************************************************************************
 * Copyright (C) 2023, 1C-Soft LLC and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     1C-Soft LLC - initial API and implementation
 *******************************************************************************/

package com.e1c.v8codestyle.bsl.check;

import static com._1c.g5.v8.dt.bsl.model.BslPackage.Literals.INVOCATION;
import static com._1c.g5.v8.dt.bsl.model.BslPackage.Literals.STRING_LITERAL__LINES;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.CONFIGURATION__ROLES;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.ROLE;

import java.text.MessageFormat;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.EList;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.IScopeProvider;

import com._1c.g5.v8.dt.bsl.model.Expression;
import com._1c.g5.v8.dt.bsl.model.Invocation;
import com._1c.g5.v8.dt.bsl.model.StaticFeatureAccess;
import com._1c.g5.v8.dt.bsl.model.StringLiteral;
import com._1c.g5.v8.dt.common.StringUtils;
import com._1c.g5.v8.dt.core.naming.ITopObjectFqnGenerator;
import com.e1c.g5.v8.dt.check.CheckComplexity;
import com.e1c.g5.v8.dt.check.ICheckParameters;
import com.e1c.g5.v8.dt.check.components.BasicCheck;
import com.e1c.g5.v8.dt.check.settings.IssueSeverity;
import com.e1c.g5.v8.dt.check.settings.IssueType;
import com.e1c.v8codestyle.check.CommonSenseCheckExtension;
import com.e1c.v8codestyle.internal.bsl.BslPlugin;
import com.google.inject.Inject;

/**
 * Check the method IsInRole, that first param contains exists roles.
 * @author Vadim Goncharov
 */
public class IsInRoleMethodRoleExistCheck
    extends BasicCheck
{

    private static final String CHECK_ID = "method-isinrole-role-exist"; //$NON-NLS-1$

    private static final String METHOD_ISINROLE_NAME = "IsInRole"; //$NON-NLS-1$

    private static final String METHOD_ISINROLE_NAME_RU = "РольДоступна"; //$NON-NLS-1$

    private final IScopeProvider scopeProvider;

    private final IQualifiedNameConverter qualifiedNameConverter;

    private final ITopObjectFqnGenerator topObjectFqnGenerator;

    /**
     * Instantiates a new invocation role check access exist role check.
     *
     * @param scopeProvider the scope provider
     * @param qualifiedNameConverter the qualified name converter
     * @param topObjectFqnGenerator the top object fqn generator
     */
    @Inject
    public IsInRoleMethodRoleExistCheck(IScopeProvider scopeProvider, IQualifiedNameConverter qualifiedNameConverter,
        ITopObjectFqnGenerator topObjectFqnGenerator)
    {
        super();
        this.scopeProvider = scopeProvider;
        this.qualifiedNameConverter = qualifiedNameConverter;
        this.topObjectFqnGenerator = topObjectFqnGenerator;
    }

    @Override
    public String getCheckId()
    {
        return CHECK_ID;
    }

    @Override
    protected void configureCheck(CheckConfigurer builder)
    {
        builder.title(Messages.IsInRoleMethodRoleExistCheck_title)
            .description(Messages.IsInRoleMethodRoleExistCheck_description)
            .complexity(CheckComplexity.NORMAL)
            .severity(IssueSeverity.MAJOR)
            .issueType(IssueType.WARNING)
            .extension(new CommonSenseCheckExtension(getCheckId(), BslPlugin.PLUGIN_ID))
            .module()
            .checkedObjectType(INVOCATION);
    }

    @Override
    protected void check(Object object, ResultAcceptor resultAcceptor, ICheckParameters parameters,
        IProgressMonitor monitor)
    {

        Invocation inv = (Invocation)object;
        if (monitor.isCanceled() || !isValidInvocation(inv))
        {
            return;
        }

        EList<Expression> params = inv.getParams();
        if (monitor.isCanceled() || params.isEmpty() || !(params.get(0) instanceof StringLiteral))
        {
            return;
        }

        StringLiteral literal = (StringLiteral)params.get(0);
        String roleName = literal.lines(true).get(0);

        if (StringUtils.isEmpty(roleName))
        {
            return;
        }

        IEObjectDescription roleDesc = getRoleDescFromScope(inv, roleName);
        if (!monitor.isCanceled() && roleDesc == null)
        {
            String message = MessageFormat
                .format(Messages.IsInRoleMethodRoleExistCheck_Role_named_not_exists_in_configuration, roleName);
            resultAcceptor.addIssue(message, literal, STRING_LITERAL__LINES);
        }

    }

    private boolean isValidInvocation(Invocation invocation)
    {

        if (invocation.getMethodAccess() instanceof StaticFeatureAccess)
        {
            StaticFeatureAccess sfa = (StaticFeatureAccess)invocation.getMethodAccess();
            if (sfa.getName().equalsIgnoreCase(METHOD_ISINROLE_NAME)
                || sfa.getName().equalsIgnoreCase(METHOD_ISINROLE_NAME_RU))
            {
                return true;
            }
        }

        return false;

    }

    private IEObjectDescription getRoleDescFromScope(Invocation inv, String roleName)
    {
        IScope scope = scopeProvider.getScope(inv, CONFIGURATION__ROLES);
        String fqn = topObjectFqnGenerator.generateStandaloneObjectFqn(ROLE, roleName);
        return scope.getSingleElement(qualifiedNameConverter.toQualifiedName(fqn));
    }

}
