/*
 * 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 com.facebook.presto.sql;

import com.facebook.presto.spi.PrestoException;
import io.airlift.joni.Regex;
import io.airlift.slice.Slice;
import io.airlift.slice.Slices;
import org.testng.annotations.Test;

import static com.facebook.presto.type.LikeFunctions.castCharToLikePattern;
import static com.facebook.presto.type.LikeFunctions.isLikePattern;
import static com.facebook.presto.type.LikeFunctions.like;
import static com.facebook.presto.type.LikeFunctions.likePattern;
import static com.facebook.presto.type.LikeFunctions.unescapeLiteralLikePattern;
import static io.airlift.slice.Slices.utf8Slice;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;

public class TestLikeFunctions
{
    @Test
    public void testLikeBasic()
    {
        Regex regex = likePattern(utf8Slice("f%b__"));
        assertTrue(like(utf8Slice("foobar"), regex));
    }

    @Test
    public void testLikeSpacesInPattern()
    {
        Regex regex = likePattern(utf8Slice("ala  "));
        assertTrue(like(utf8Slice("ala  "), regex));
        assertFalse(like(utf8Slice("ala"), regex));

        regex = castCharToLikePattern(5L, utf8Slice("ala"));
        assertTrue(like(utf8Slice("ala  "), regex));
        assertFalse(like(utf8Slice("ala"), regex));
    }

    @Test
    public void testLikeNewlineInPattern()
    {
        Regex regex = likePattern(utf8Slice("%o\nbar"));
        assertTrue(like(utf8Slice("foo\nbar"), regex));
    }

    @Test
    public void testLikeNewlineBeforeMatch()
    {
        Regex regex = likePattern(utf8Slice("%b%"));
        assertTrue(like(utf8Slice("foo\nbar"), regex));
    }

    @Test
    public void testLikeNewlineInMatch()
    {
        Regex regex = likePattern(utf8Slice("f%b%"));
        assertTrue(like(utf8Slice("foo\nbar"), regex));
    }

    @Test(timeOut = 1000)
    public void testLikeUtf8Pattern()
    {
        Regex regex = likePattern(utf8Slice("%\u540d\u8a89%"), utf8Slice("\\"));
        assertFalse(like(utf8Slice("foo"), regex));
    }

    @SuppressWarnings("NumericCastThatLosesPrecision")
    @Test(timeOut = 1000)
    public void testLikeInvalidUtf8Value()
    {
        Slice value = Slices.wrappedBuffer(new byte[] {'a', 'b', 'c', (byte) 0xFF, 'x', 'y'});
        Regex regex = likePattern(utf8Slice("%b%"), utf8Slice("\\"));
        assertTrue(like(value, regex));
    }

    @Test
    public void testBackslashesNoSpecialTreatment()
            throws Exception
    {
        Regex regex = likePattern(utf8Slice("\\abc\\/\\\\"));
        assertTrue(like(utf8Slice("\\abc\\/\\\\"), regex));
    }

    @Test
    public void testSelfEscaping()
            throws Exception
    {
        Regex regex = likePattern(utf8Slice("\\\\abc\\%"), utf8Slice("\\"));
        assertTrue(like(utf8Slice("\\abc%"), regex));
    }

    @Test
    public void testAlternateEscapedCharacters()
            throws Exception
    {
        Regex regex = likePattern(utf8Slice("xxx%x_abcxx"), utf8Slice("x"));
        assertTrue(like(utf8Slice("x%_abcx"), regex));
    }

    @Test
    public void testInvalidLikePattern()
    {
        assertThrows(PrestoException.class, () -> likePattern(utf8Slice("#"), utf8Slice("#")));
        assertThrows(PrestoException.class, () -> likePattern(utf8Slice("abc#abc"), utf8Slice("#")));
        assertThrows(PrestoException.class, () -> likePattern(utf8Slice("abc#"), utf8Slice("#")));
    }

    @Test
    public void testIsLikePattern()
    {
        assertFalse(isLikePattern(utf8Slice("abc"), null));
        assertFalse(isLikePattern(utf8Slice("abc#_def"), utf8Slice("#")));
        assertFalse(isLikePattern(utf8Slice("abc##def"), utf8Slice("#")));
        assertFalse(isLikePattern(utf8Slice("abc#%def"), utf8Slice("#")));
        assertTrue(isLikePattern(utf8Slice("abc%def"), null));
        assertTrue(isLikePattern(utf8Slice("abcdef_"), null));
        assertTrue(isLikePattern(utf8Slice("abcdef##_"), utf8Slice("#")));
        assertTrue(isLikePattern(utf8Slice("%abcdef#_"), utf8Slice("#")));
        assertThrows(PrestoException.class, () -> isLikePattern(utf8Slice("#"), utf8Slice("#")));
        assertThrows(PrestoException.class, () -> isLikePattern(utf8Slice("abc#abc"), utf8Slice("#")));
        assertThrows(PrestoException.class, () -> isLikePattern(utf8Slice("abc#"), utf8Slice("#")));
    }

    @Test
    public void testUnescapeValidLikePattern()
    {
        assertEquals(unescapeLiteralLikePattern(utf8Slice("abc"), null), utf8Slice("abc"));
        assertEquals(unescapeLiteralLikePattern(utf8Slice("abc#_"), utf8Slice("#")), utf8Slice("abc_"));
        assertEquals(unescapeLiteralLikePattern(utf8Slice("a##bc#_"), utf8Slice("#")), utf8Slice("a#bc_"));
        assertEquals(unescapeLiteralLikePattern(utf8Slice("a###_bc"), utf8Slice("#")), utf8Slice("a#_bc"));
    }
}
