//
//  ========================================================================
//  Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.util.resource;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.log.Log;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(WorkDirExtension.class)
public class ResourceAliasTest
{
    public WorkDir workDir;

    public static Stream<Function<Path, Resource>> resourceTypes()
    {
        List<Function<Path, Resource>> types = new ArrayList<>();

        types.add((path) -> new PathResource(path));
        types.add((path) -> new FileResource(path.toFile()));

        return types.stream();
    }

    @ParameterizedTest
    @MethodSource("resourceTypes")
    public void testPercentPaths(Function<Path, Resource> resourceType) throws IOException
    {
        Path baseDir = workDir.getEmptyPathDir();

        Path foo = baseDir.resolve("%foo");
        Files.createDirectories(foo);

        Path bar = foo.resolve("bar%");
        Files.createDirectories(bar);

        Path text = bar.resolve("test.txt");
        FS.touch(text);

        // At this point we have a path .../%foo/bar%/test.txt present on the filesystem.
        // This would also apply for paths found in JAR files (like META-INF/resources/%foo/bar%/test.txt)

        assertTrue(Files.exists(text));

        Resource baseResource = resourceType.apply(baseDir);
        assertTrue(baseResource.exists(), "baseResource exists");

        Resource fooResource = baseResource.addPath("%foo");
        assertTrue(fooResource.exists(), "fooResource exists");
        assertTrue(fooResource.isDirectory(), "fooResource isDir");
        if (fooResource instanceof FileResource)
            assertTrue(fooResource.isAlias(), "fooResource isAlias");
        else
            assertFalse(fooResource.isAlias(), "fooResource isAlias");

        Resource barResource = fooResource.addPath("bar%");
        assertTrue(barResource.exists(), "barResource exists");
        assertTrue(barResource.isDirectory(), "barResource isDir");
        if (fooResource instanceof FileResource)
            assertTrue(barResource.isAlias(), "barResource isAlias");
        else
            assertFalse(barResource.isAlias(), "barResource isAlias");

        Resource textResource = barResource.addPath("test.txt");
        assertTrue(textResource.exists(), "textResource exists");
        assertFalse(textResource.isDirectory(), "textResource isDir");
    }

    @Test
    public void testNullCharEndingFilename() throws Exception
    {
        Path baseDir = workDir.getEmptyPathDir();

        Path file = baseDir.resolve("test.txt");
        FS.touch(file);

        try
        {
            Path file0 = baseDir.resolve("test.txt\0");
            if (!Files.exists(file0))
                return;  // this file system does get tricked by ending filenames

            assertThat(file0 + " exists", Files.exists(file0), is(true));  // This is an alias!

            Resource dir = Resource.newResource(baseDir);

            // Test not alias paths
            Resource resource = Resource.newResource(file);
            assertTrue(resource.exists());
            assertNull(resource.getAlias());
            resource = Resource.newResource(file.toAbsolutePath());
            assertTrue(resource.exists());
            assertNull(resource.getAlias());
            resource = Resource.newResource(file.toUri());
            assertTrue(resource.exists());
            assertNull(resource.getAlias());
            resource = Resource.newResource(file.toUri().toString());
            assertTrue(resource.exists());
            assertNull(resource.getAlias());
            resource = dir.addPath("test.txt");
            assertTrue(resource.exists());
            assertNull(resource.getAlias());

            // Test alias paths
            resource = Resource.newResource(file0);
            assertTrue(resource.exists());
            assertNotNull(resource.getAlias());
            resource = Resource.newResource(file0.toAbsolutePath());
            assertTrue(resource.exists());
            assertNotNull(resource.getAlias());
            resource = Resource.newResource(file0.toUri());
            assertTrue(resource.exists());
            assertNotNull(resource.getAlias());
            resource = Resource.newResource(file0.toUri().toString());
            assertTrue(resource.exists());
            assertNotNull(resource.getAlias());

            try
            {
                resource = dir.addPath("test.txt\0");
                assertTrue(resource.exists());
                assertNotNull(resource.getAlias());
            }
            catch (MalformedURLException e)
            {
                assertTrue(true);
            }
        }
        catch (InvalidPathException e)
        {
            // this file system does allow null char ending filenames
            Log.getRootLogger().ignore(e);
        }
    }
}
