package org.xbib.files.ftp.test.parser;

import static org.junit.jupiter.api.Assertions.assertEquals;
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;
import org.junit.jupiter.api.Test;
import org.xbib.files.ftp.FTPFile;
import java.time.Month;
import java.time.Year;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.EnumSet;
import org.xbib.files.ftp.parser.MacOsPeterFTPEntryParser;

public class MacOsPeterFTPEntryParserTest {

    private static final String[] badsamples = {
            "drwxr-xr-x    123       folder        0 Jan  4 14:49 Steak",
    };

    private static final String[] goodsamples = {
            "-rw-r--r--    54149       27826    81975 Jul 22  2010 09.jpg",
            "drwxr-xr-x               folder        0 Jan  4 14:51 Alias_to_Steak",
            "-rw-r--r--    78440       49231   127671 Jul 22  2010 Filename with whitespace.jpg",
            "-rw-r--r--    78440       49231   127671 Jul 22 14:51 Filename with whitespace.jpg",
            "-rw-r--r--        0      108767   108767 Jul 22  2010 presentation03.jpg",
            "-rw-r--r--    58679       60393   119072 Jul 22  2010 presentation04.jpg",
            "-rw-r--r--    82543       51433   133976 Jul 22  2010 presentation06.jpg",
            "-rw-r--r--    83616     1430976  1514592 Jul 22  2010 presentation10.jpg",
            "-rw-r--r--        0       66990    66990 Jul 22  2010 presentation11.jpg",
            "drwxr-xr-x               folder        0 Jan  4 14:49 Steak",
            "-rwx------        0       12713    12713 Jul  8  2009 Twitter_Avatar.png",
    };

    @Test
    public void testBadListing() {
        MacOsPeterFTPEntryParser parser = new MacOsPeterFTPEntryParser();
        for (String test : badsamples) {
            try {
                FTPFile f = parser.parseFTPEntry(test);
                assertNull(nullFileOrNullDate(f), "Should have Failed to parse <" + test + ">");
            } catch (DateTimeParseException e) {
                //
            }
        }
    }

    private FTPFile nullFileOrNullDate(FTPFile f) {
        if (f == null) {
            return null;
        }
        if (f.getTimestamp() == null) {
            return null;
        }
        return f;
    }

    @Test
    public void testGoodListing() {
        MacOsPeterFTPEntryParser parser = new MacOsPeterFTPEntryParser();
        for (String test : goodsamples) {
            FTPFile f = parser.parseFTPEntry(test);
            assertNotNull(f, "Failed to parse " + test);
        }
    }
    @Test
    public void testParseFieldsOnDirectory() throws Exception {
        MacOsPeterFTPEntryParser parser = new MacOsPeterFTPEntryParser();
        FTPFile f = parser.parseFTPEntry("drwxr-xr-x               folder        0 Mar  2 15:13 Alias_to_Steak");
        assertNotNull(f, "Could not parse entry.");
        assertTrue(f.isDirectory(), "Should have been a directory.");
        checkPermissions(f);
        assertEquals(0, f.getHardLinkCount());
        assertNull(f.getUser());
        assertNull(f.getGroup());
        assertEquals(0, f.getSize());
        assertEquals("Alias_to_Steak", f.getName());
        ZonedDateTime zonedDateTime = ZonedDateTime.of(Year.now().getValue(),
                Month.MARCH.getValue(), 2, 15, 13, 0, 0, ZoneId.of("UTC"));
        if (zonedDateTime.isAfter(ZonedDateTime.now())) {
            zonedDateTime = zonedDateTime.minusYears(1);
        }
        assertEquals(zonedDateTime, f.getTimestamp());
    }

    @Test
    public void testParseFieldsOnFile() throws Exception {
        MacOsPeterFTPEntryParser parser = new MacOsPeterFTPEntryParser();
        FTPFile f = parser.parseFTPEntry("-rwxr-xr-x    78440       49231   127671 Jul  2 14:51 Filename with whitespace.jpg");
        assertNotNull(f, "Could not parse entry.");
        assertTrue(f.isFile(), "Should have been a file.");
        checkPermissions(f);
        assertEquals(0, f.getHardLinkCount());
        assertNull(f.getUser());
        assertNull(f.getGroup());
        assertEquals("Filename with whitespace.jpg", f.getName());
        assertEquals(127671L, f.getSize());
        ZonedDateTime zonedDateTime = ZonedDateTime.of(Year.now().getValue(),
                Month.JULY.getValue(), 2, 14, 51, 0, 0, ZoneId.of("UTC"));
        if (zonedDateTime.isAfter(ZonedDateTime.now())) {
            zonedDateTime = zonedDateTime.minusYears(1);
        }
        assertEquals(zonedDateTime, f.getTimestamp());
    }

    private void checkPermissions(FTPFile f) {
        assertTrue(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION), "Should have user read permission.");
        assertTrue(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION), "Should have user write permission.");
        assertTrue(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION), "Should have user execute permission.");
        assertTrue(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION), "Should have group read permission.");
        assertFalse(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION), "Should NOT have group write permission.");
        assertTrue(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION), "Should have group execute permission.");
        assertTrue(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION),"Should have world read permission.");
        assertFalse(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION), "Should NOT have world write permission.");
        assertTrue(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION), "Should have world execute permission.");
    }

    @Test
    public void testDefaultPrecision() {
        testPrecision("-rw-r--r--    78440       49231   127671 Jul 22  2010 Filename with whitespace.jpg",
                EnumSet.of(ChronoUnit.DAYS));
    }

    @Test
    public void testRecentPrecision() {
        testPrecision("-rw-r--r--    78440       49231   127671 Jul 22 14:51 Filename with whitespace.jpg",
                EnumSet.of(ChronoUnit.MINUTES, ChronoUnit.HOURS, ChronoUnit.DAYS, ChronoUnit.YEARS));
    }

    private void testPrecision(String listEntry, EnumSet<ChronoUnit> units) {
        MacOsPeterFTPEntryParser parser = new MacOsPeterFTPEntryParser();
        FTPFile file = parser.parseFTPEntry(listEntry);
        assertNotNull(file, "Could not parse " + listEntry);
        ZonedDateTime zonedDateTime = file.getTimestamp();
        assertNotNull(zonedDateTime, "Failed to parse time in " + listEntry);
        for (ChronoUnit unit : units) {
            assertTrue(zonedDateTime.isSupported(unit), "Expected set " + unit + " in " + listEntry);
        }
    }
}
