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.Disabled;
import org.junit.jupiter.api.Test;
import org.xbib.files.ftp.FTPClientConfig;
import org.xbib.files.ftp.FTPFile;
import org.xbib.files.ftp.FTPFileEntryParser;
import java.time.Month;
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.CompositeFileEntryParser;
import org.xbib.files.ftp.parser.OS400FTPEntryParser;
import org.xbib.files.ftp.parser.UnixFTPEntryParser;

public class OS400FTPEntryParserTest {

    private static final String[][] badsamples = {
            {
                    "PEP              4019 04/03/18 18:58:16 STMF       einladung.zip",
                    "PEP               422 03/24 14:06:26 *STMF      readme",
                    "PEP              6409 04/03/24 30:06:29 *STMF      build.xml",
                    "PEP USR         36864 04/03/24 14:06:34 *DIR       dir1/",
                    "PEP             3686404/03/24 14:06:47 *DIR       zdir2/"
            },
            {
                    "----rwxr-x   1PEP       0           4019 Mar 18 18:58 einladung.zip",
                    "----rwxr-x   1 PEP      0  xx        422 Mar 24 14:06 readme",
                    "----rwxr-x   1 PEP      0           8492 Apr 07 30:13 build.xml",
                    "d---rwxr-x   2 PEP      0          45056Mar 24 14:06 zdir2"
            }
    };

    private static final String[][] goodsamples = {
            {
                    "PEP              4019 04/03/18 18:58:16 *STMF      einladung.zip",
                    "PEP               422 04/03/24 14:06:26 *STMF      readme",
                    "PEP              6409 04/03/24 14:06:29 *STMF      build.xml",
                    "PEP             36864 04/03/24 14:06:34 *DIR       dir1/",
                    "PEP             36864 04/03/24 14:06:47 *DIR       zdir2/"
            },
            {
                   // "----rwxr-x   1 PEP      0           4019 Mar 18 18:58 einladung.zip",
                   // "----rwxr-x   1 PEP      0            422 Mar 24 14:06 readme",
                   // "----rwxr-x   1 PEP      0           8492 Apr 07 07:13 build.xml",
                   // "d---rwxr-x   2 PEP      0          45056 Mar 24 14:06 dir1",
                   // "d---rwxr-x   2 PEP      0          45056 Mar 24 14:06 zdir2"
            }
    };

    @Test
    public void testBadListing() {
        CompositeFileEntryParser parser = new CompositeFileEntryParser(new FTPFileEntryParser[]{new OS400FTPEntryParser(), new UnixFTPEntryParser()});
        for (String[] badsample : badsamples) {
            for (String test : badsample) {
                try {
                    FTPFile f = parser.parseFTPEntry(test);
                    assertNull(nullFileOrNullDate(f), "Should have Failed to parse " + test);
                    //doAdditionalBadTests(test, f);
                } catch (DateTimeParseException e) {
                    //
                }
            }
        }
    }

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

    @Test
    public void testConsistentListing() {
        CompositeFileEntryParser parser = new CompositeFileEntryParser(new FTPFileEntryParser[]{new OS400FTPEntryParser(), new UnixFTPEntryParser()});
        for (String[] goodsample : goodsamples) {
            for (String test : goodsample) {
                FTPFile f = parser.parseFTPEntry(test);
                assertNotNull(f, "Failed to parse " + test);
                doAdditionalGoodTests(test, f);
            }
        }
    }

    @Test
    @Disabled
    public void testInconsistentListing() {
        CompositeFileEntryParser parser = new CompositeFileEntryParser(new FTPFileEntryParser[]{new OS400FTPEntryParser(), new UnixFTPEntryParser()});
        for (int i = 0; i < goodsamples.length; i++) {
            String test = goodsamples[i][0];
            FTPFile f = parser.parseFTPEntry(test);
            switch (i) {
                case 0:
                    assertNotNull(f, "Failed to parse " + test);
                    break;
                case 1:
                    assertNull(f, "Should have failed to parse " + test);
                    break;
            }
        }
    }

    @Test
    public void testParseFieldsOnDirectory() {
        CompositeFileEntryParser parser = new CompositeFileEntryParser(new FTPFileEntryParser[]{new OS400FTPEntryParser(), new UnixFTPEntryParser()});
        FTPFile f = parser.parseFTPEntry("PEP             36864 04/03/24 14:06:34 *DIR       dir1/");
        assertNotNull(f, "Could not parse entry.");
        assertTrue(f.isDirectory(), "Should have been a directory.");
        assertEquals("PEP", f.getUser());
        assertEquals("dir1", f.getName());
        assertEquals(36864, f.getSize());
        ZonedDateTime zonedDateTime = ZonedDateTime.of(2004,
                Month.MARCH.getValue(), 24, 14, 6, 34, 0, ZoneId.of("UTC"));
        assertEquals(zonedDateTime, f.getTimestamp());
    }

    private void doAdditionalGoodTests(String test, FTPFile f) {
        if (test.startsWith("d")) {
            assertEquals(FTPFile.DIRECTORY_TYPE, f.getType(), "directory.type");
        }
    }

    @Test
    public void testParseFieldsOnFile() throws Exception {
        CompositeFileEntryParser parser = new CompositeFileEntryParser(new FTPFileEntryParser[]{new OS400FTPEntryParser(), new UnixFTPEntryParser()});
        FTPFile f = parser.parseFTPEntry("PEP              5000000000 04/03/24 14:06:29 *STMF      build.xml");
        assertNotNull(f,"Could not parse entry.");
        assertTrue(f.isFile(), "Should have been a file.");
        assertEquals("PEP", f.getUser());
        assertEquals("build.xml", f.getName());
        assertEquals(5000000000L, f.getSize());
        ZonedDateTime zonedDateTime = ZonedDateTime.of(2004,
                Month.MARCH.getValue(), 24, 14, 6, 29, 0, ZoneId.of("UTC"));

        assertEquals(zonedDateTime, f.getTimestamp());
    }

    @Test
    public void testDefaultPrecision() {
        testPrecision("PEP              4019 04/03/18 18:58:16 *STMF      einladung.zip",
                EnumSet.of(ChronoUnit.SECONDS));
    }

    @Test
    public void testRecentPrecision() {
        testPrecision("----rwxr-x   1 PEP      0           4019 Mar 18 18:58 einladung.zip",
                EnumSet.of(ChronoUnit.MINUTES));
    }

    private void testPrecision(String listEntry, EnumSet<ChronoUnit> units) {
        CompositeFileEntryParser parser = new CompositeFileEntryParser(new FTPFileEntryParser[]{new OS400FTPEntryParser(), new UnixFTPEntryParser()});
        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);
        }
    }

    @Test
    public void testNET573() {
        final FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_AS400);
        conf.setDefaultDateFormatStr("MM/dd/yy HH:mm:ss");
        final FTPFileEntryParser parser = new OS400FTPEntryParser(conf);
        FTPFile f = parser.parseFTPEntry("ZFTPDEV 9069 05/20/15 15:36:52 *STMF /DRV/AUDWRKSHET/AUDWRK0204232015114625.PDF");
        assertNotNull(f, "Could not parse entry.");
        assertNotNull(f.getTimestamp(), "Could not parse timestamp.");
        assertFalse(f.isDirectory(), "Should not have been a directory.");
        assertEquals("ZFTPDEV", f.getUser());
        assertEquals("AUDWRK0204232015114625.PDF", f.getName());
        assertEquals(9069, f.getSize());
        ZonedDateTime zonedDateTime = ZonedDateTime.of(2015,
                Month.MAY.getValue(), 20, 15, 36, 52, 0, ZoneId.of("UTC"));
        assertEquals(zonedDateTime, f.getTimestamp());
    }

}
