/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2023 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.apache.commons.lang3.RandomStringUtils;
import org.jboss.installer.core.ValidationResult;
import org.jboss.installer.test.utils.MockLanguageUtils;
import org.junit.Before;
import org.junit.Test;

import java.nio.file.Path;

import static org.jboss.installer.validators.PathValidator.EAP_RESERVED_PATH_LENGTH;
import static org.jboss.installer.validators.WindowsPathValidator.WIN_PATH_LENGTH_HARD_LIMIT;
import static org.jboss.installer.validators.WindowsPathValidator.WIN_PATH_LENGTH_SOFT_LIMIT;
import static org.junit.Assert.*;

public class WindowsPathValidatorTest {

    private WindowsPathValidator validator;

    @Before
    public void setUp() throws Exception {
        validator = new WindowsPathValidator("test", new MockLanguageUtils());
    }

    @Test
    public void testValidAbsolutePath() throws Exception {
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("C:\\Some\\Path"));
    }

    @Test
    public void testValidAbsolutePathWithoutLeadingSlash() throws Exception {
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("C:Some\\Path"));
    }

    @Test
    public void testValidPathWithForwardSlash() throws Exception {
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("C:/Some/Path"));
    }

    @Test
    public void testInvalidDriveName() throws Exception {
        assertResult(ValidationResult.Result.ERROR, validator.validateCharacters("FOO:\\Some\\Path"));
    }

    @Test
    public void testValidCurrentDrivePath() throws Exception {
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("\\Some\\Path"));
    }

    @Test
    public void testValidRelativeDrivePath() throws Exception {
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("Some\\Path"));
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("..\\Some\\Path"));
    }

    @Test
    public void testValidUNCPath() throws Exception {
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("\\\\system07\\C$\\"));
    }

    @Test
    public void testValidUNCPathWithForwardSlashes() throws Exception {
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("//system07\\C$\\"));
    }

    @Test
    public void testPathWithColon() throws Exception {
        assertResult(ValidationResult.Result.ERROR, validator.validateCharacters("Some\\P:ath"));
    }

    @Test
    public void testPathWithDoubleSlash() throws Exception {
        assertResult(ValidationResult.Result.ERROR, validator.validateCharacters("Some\\\\Path"));
    }

    @Test
    public void testValidPathWithSingleSpaces() throws Exception {
        assertResult(ValidationResult.Result.OK, validator.validateCharacters("Some\\Pa th\\He re"));
    }

    @Test
    public void testInvalidPathWithDoubleSpaces() throws Exception {
        assertResult(ValidationResult.Result.ERROR, validator.validateCharacters("Some\\Pa  th\\He re"));
    }

    @Test
    public void testInvalidPathWithTrippleSpaces() throws Exception {
        assertResult(ValidationResult.Result.ERROR, validator.validateCharacters("Some\\Pa   th\\He re"));
    }

    @Test
    public void testPathsWithInvalidCharacters() throws Exception {
        for (String s: WindowsPathValidator.WINDOWS_INVALID_CHARACTERS) {
            assertResult(ValidationResult.Result.ERROR, validator.validateCharacters("C:\\Some\\Pa" + s + "th"));
        }
    }

    @Test
    public void testPathsWithRestrictedCharacters() throws Exception {
        for (String filename: WindowsPathValidator.WINDOWS_RESTRICTED_FILENAMES) {
            assertResult(ValidationResult.Result.ERROR, validator.validateCharacters("C:\\Some\\" + filename));
        }
    }

    @Test
    public void testPathsWithRestrictedCharactersWithForwardSlashes() throws Exception {
        for (String filename: WindowsPathValidator.WINDOWS_RESTRICTED_FILENAMES) {
            assertResult(ValidationResult.Result.ERROR, validator.validateCharacters("C:/Some/" + filename));
        }
    }

    @Test
    public void testWarningOnExceedSoftLimit() throws Exception {
        final int maxLength = WIN_PATH_LENGTH_SOFT_LIMIT - EAP_RESERVED_PATH_LENGTH;
        final StringBuilder sb = buildPath(50, maxLength);

        assertResult(ValidationResult.Result.WARNING, validator.validateAbsoluteLength(Path.of(sb.toString())));
    }

    @Test
    public void testExactlySoftLimitIsOK() throws Exception {
        final int filenameLength = 50;
        final int maxLength = WIN_PATH_LENGTH_SOFT_LIMIT - EAP_RESERVED_PATH_LENGTH - filenameLength;

        final StringBuilder sb = buildPath(filenameLength, maxLength);
        sb.append("\\").append(RandomStringUtils.randomAlphabetic(filenameLength + (maxLength - sb.length())));

        assertResult(ValidationResult.Result.OK, validator.validateAbsoluteLength(Path.of(sb.toString())));
    }

    @Test
    public void testErrorOnExceedHardLimit() throws Exception {
        final int maxLength = WIN_PATH_LENGTH_HARD_LIMIT - EAP_RESERVED_PATH_LENGTH;
        final StringBuilder sb = buildPath(50, maxLength);

        assertResult(ValidationResult.Result.ERROR, validator.validateAbsoluteLength(Path.of(sb.toString())));
    }

    @Test
    public void testWarnOnExactlyHardLimit() throws Exception {
        final int filenameLength = 50;
        final int maxLength = WIN_PATH_LENGTH_HARD_LIMIT - EAP_RESERVED_PATH_LENGTH - filenameLength;

        final StringBuilder sb = buildPath(filenameLength, maxLength);
        sb.append("\\").append(RandomStringUtils.randomAlphabetic(filenameLength + (maxLength - sb.length())));

        assertResult(ValidationResult.Result.WARNING, validator.validateAbsoluteLength(Path.of(sb.toString())));
    }

    private static StringBuilder buildPath(int filenameLength, int maxLength) {
        final String filename = RandomStringUtils.randomAlphabetic(50);
        final StringBuilder sb = new StringBuilder("C:");
        while (sb.length() < maxLength) {
            sb.append("\\").append(filename);
        }
        return sb;
    }

    private static void assertResult(ValidationResult.Result expected, ValidationResult validationResult) {
        assertEquals(expected, validationResult.getResult());
    }

}