package be.iminds.ilabt.jfed.util;

import org.testng.Assert;
import org.testng.annotations.Test;

import java.text.ParseException;
import java.util.Date;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

/**
 * RFC3339 unit tests
 */
public class RFC3339UtilTest {

    @Test
    public void testISO8601a() throws ParseException {
        //actual date seen in foam_expires
        Date actual = RFC3339Util.iso8601StringToDate("2015-04-06 00:00:03+00:00");
        Date expected = RFC3339Util.rfc3339StringToDate("2015-04-06T00:00:03Z");
        assertThat(actual, equalTo(expected));
    }
    @Test
    public void testISO8601b() throws ParseException {
        Date actual = RFC3339Util.iso8601StringToDate("2015-04-06 00:00:03Z");
        Date expected = RFC3339Util.rfc3339StringToDate("2015-04-06T00:00:03Z");
        assertThat(actual, equalTo(expected));
    }


    @Test
    public void testRFC3339() throws ParseException {
        //default values
        testRFC3339Internal(false/*forceZulu*/, true/*forceZinsteadOfZero*/);
    }

    @Test
    public void testRFC3339Variation1() throws ParseException {
        //default values
        testRFC3339Internal(true/*forceZulu*/, true/*forceZinsteadOfZero*/);
    }

    @Test
    public void testRFC3339Variation2() throws ParseException {
        //default values
        testRFC3339Internal(false/*forceZulu*/, false/*forceZinsteadOfZero*/);
    }

    @Test
    public void testRFC3339Variation3() throws ParseException {
        //default values
        testRFC3339Internal(true/*forceZulu*/, false/*forceZinsteadOfZero*/);
    }

    @Test
    public void testRFC3339Variation4() throws ParseException {
        //default values
        testRFC3339InternalNoSubsecond(false/*forceZulu*/, true/*forceZinsteadOfZero*/);
    }

    @Test
    public void testRFC3339Variation5() throws ParseException {
        //default values
        testRFC3339InternalNoSubsecond(true/*forceZulu*/, false/*forceZinsteadOfZero*/);
    }

    @Test
    public void testRFC3339Variation6() throws ParseException {
        //default values
        testRFC3339InternalNoSubsecond(true/*forceZulu*/, true/*forceZinsteadOfZero*/);
    }

    @Test
    public void testRFC3339Variation7() throws ParseException {
        //default values
        testRFC3339InternalNoSubsecond(false/*forceZulu*/, false/*forceZinsteadOfZero*/);
    }

    public void testRFC3339Internal(boolean expiredate_forcezulu,
                                    boolean expiredate_forceZinsteadOfZero) throws ParseException {
        String a1 = "1996-12-19T16:39:57.25-08:00";
        String a2 = "1996-12-20T00:39:57.25Z";
        String a3 = "1996-12-20T00:39:57.25+00:00";
        String b1 = "1990-12-31T23:59:60Z";
        String b2 = "1990-12-31T15:59:60-08:00";
        String c = "2013-04-22T05:18:52Z";
        String d = "2013-11-28T15:07:57Z";


        String a1alt = "1996-12-19t16:39:57.25-08:00";
        String a2alt = "1996-12-20t00:39:57.25Z";
        String a3alt = "1996-12-20 00:39:57.25z";

        /*more valid examples:
            2002-10-02T10:00:00-05:00
            2002-10-02T15:00:00Z
            2002-10-02T15:00:00.05Z

            1985-04-12T23:20:50.52Z
            1996-12-19T16:39:57-08:00 equals 1996-12-20T00:39:57Z

            leap seconds:
             1990-12-31T23:59:60Z equals 1990-12-31T15:59:60-08:00
        * */

        Date a1d = RFC3339Util.rfc3339StringToDate(a1);
        Date a2d = RFC3339Util.rfc3339StringToDate(a2);
        Date a3d = RFC3339Util.rfc3339StringToDate(a3);
        Date a1altd = RFC3339Util.rfc3339StringToDate(a1alt);
        Date a2altd = RFC3339Util.rfc3339StringToDate(a2alt);
        Date a3altd = RFC3339Util.rfc3339StringToDate(a3alt);
        Date b1d = RFC3339Util.rfc3339StringToDate(b1);
        Date b2d = RFC3339Util.rfc3339StringToDate(b2);
        Date cd = RFC3339Util.rfc3339StringToDate(c);
        Date dd = RFC3339Util.rfc3339StringToDate(d);

        Assert.assertEquals(a1d, a1altd);
        Assert.assertEquals(a2d, a2altd);
        Assert.assertEquals(a3d, a3altd);

        String str_a1r =
                RFC3339Util.dateToRFC3339String(a1d, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero);
        String str_a1r2 =
                RFC3339Util.dateToRFC3339String(a1d, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero).toLowerCase();
        String str_a1r3 =
                RFC3339Util.dateToRFC3339String(a1d, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero).replace('T', ' ');
        String str_a2r =
                RFC3339Util.dateToRFC3339String(a2d, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero);
        String str_a3r =
                RFC3339Util.dateToRFC3339String(a3d, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero);
        String str_b1r =
                RFC3339Util.dateToRFC3339String(b1d, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero);
        String str_b2r =
                RFC3339Util.dateToRFC3339String(b2d, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero);
        String str_cr =
                RFC3339Util.dateToRFC3339String(cd, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero);
        String str_dr =
                RFC3339Util.dateToRFC3339String(dd, expiredate_forcezulu, false, expiredate_forceZinsteadOfZero);

        System.out.println("Reconstructed a1 str: " + str_a1r);
        System.out.println("Reconstructed a1 str: " + str_a1r2);
        System.out.println("Reconstructed a1 str: " + str_a1r3);

        Date a1dr = RFC3339Util.rfc3339StringToDate(str_a1r);
        Date a1dr2 = RFC3339Util.rfc3339StringToDate(str_a1r2);
        Date a1dr3 = RFC3339Util.rfc3339StringToDate(str_a1r3);
        Date a2dr = RFC3339Util.rfc3339StringToDate(str_a2r);
        Date a3dr = RFC3339Util.rfc3339StringToDate(str_a3r);
        Date b1dr = RFC3339Util.rfc3339StringToDate(str_b1r);
        Date b2dr = RFC3339Util.rfc3339StringToDate(str_b2r);
        Date cdr = RFC3339Util.rfc3339StringToDate(str_cr);
        Date ddr = RFC3339Util.rfc3339StringToDate(str_dr);

        Assert.assertEquals(a1d, a2d);
        Assert.assertEquals(a1d, a3d);
        Assert.assertEquals(b1d, b2d);

        Assert.assertEquals(a1dr, a1d);
        Assert.assertEquals(a1dr2, a1d);
        Assert.assertEquals(a1dr3, a1d);
        Assert.assertEquals(a2dr, a2d);
        Assert.assertEquals(a3dr, a3d);
        Assert.assertEquals(b1dr, b1d);
        Assert.assertEquals(b2dr, b2d);
        Assert.assertEquals(cdr, cd);
        Assert.assertEquals(ddr, dd);


        //compare with iso8601StringToDate
        Date iso8601a1d = RFC3339Util.iso8601StringToDate(a1);
        Date iso8601a2d = RFC3339Util.iso8601StringToDate(a2);
        Date iso8601a3d = RFC3339Util.iso8601StringToDate(a3);
        Date iso8601a1altd = RFC3339Util.iso8601StringToDate(a1alt);
        Date iso8601a2altd = RFC3339Util.iso8601StringToDate(a2alt);
        Date iso8601a3altd = RFC3339Util.iso8601StringToDate(a3alt);
        Date iso8601b1d = RFC3339Util.iso8601StringToDate(b1);
        Date iso8601b2d = RFC3339Util.iso8601StringToDate(b2);
        Date iso8601cd = RFC3339Util.iso8601StringToDate(c);
        Date iso8601dd = RFC3339Util.iso8601StringToDate(d);

        Assert.assertEquals(iso8601a1d.getTime(), a1d.getTime());
        Assert.assertEquals(iso8601a2d.getTime(), a2d.getTime());
        Assert.assertEquals(iso8601a3d.getTime(), a3d.getTime());
        Assert.assertEquals(iso8601a1altd.getTime(), a1altd.getTime());
        Assert.assertEquals(iso8601a2altd.getTime(), a2altd.getTime());
        Assert.assertEquals(iso8601a3altd.getTime(), a3altd.getTime());
        Assert.assertEquals(iso8601b1d.getTime(), b1d.getTime());
        Assert.assertEquals(iso8601b2d.getTime(), b2d.getTime());
        Assert.assertEquals(iso8601cd.getTime(), cd.getTime());
        Assert.assertEquals(iso8601dd.getTime(), dd.getTime());


//        //iso8601 also supports freaky times without timezone. Should see it as UTC or localtime in that case. (which is confusing off course)
//        String cNoZ = "2013-04-22T05:18:52";
//        Date iso8601cNoZd = RFC3339Util.iso8601StringToDate(cNoZ);
//        Assert.assertEquals(iso8601cd, iso8601cNoZd);
//        Assert.assertEquals(iso8601cd.getTime(), iso8601cNoZd.getTime());
    }

    public void testRFC3339InternalNoSubsecond(boolean expiredate_forcezulu, boolean expiredate_forceZinsteadOfZero) throws ParseException {
        String a1 = "1996-12-19T16:39:57.25-08:00";
        String a2 = "1996-12-20T00:39:57.25Z";
        String a3 = "1996-12-20T00:39:57.25+00:00";
        String b1 = "1990-12-31T23:59:60Z";
        String b2 = "1990-12-31T15:59:60-08:00";
        String c = "2013-04-22T05:18:52Z";

        String a1alt = "1996-12-19t16:39:57.25-08:00";
        String a2alt = "1996-12-20t00:39:57.25Z";
        String a3alt = "1996-12-20 00:39:57.25z";

        Date a1d = RFC3339Util.rfc3339StringToDate(a1);
        Date a2d = RFC3339Util.rfc3339StringToDate(a2);
        Date a3d = RFC3339Util.rfc3339StringToDate(a3);
        Date a1altd = RFC3339Util.rfc3339StringToDate(a1alt);
        Date a2altd = RFC3339Util.rfc3339StringToDate(a2alt);
        Date a3altd = RFC3339Util.rfc3339StringToDate(a3alt);
        Date b1d = RFC3339Util.rfc3339StringToDate(b1);
        Date b2d = RFC3339Util.rfc3339StringToDate(b2);
        Date cd = RFC3339Util.rfc3339StringToDate(c);

        String noSubSecond_a1 = "1996-12-19T16:39:57-08:00";
        String noSubSecond_a2 = "1996-12-20T00:39:57Z";
        String noSubSecond_a3 = "1996-12-20T00:39:57+00:00";
        String noSubSecond_b1 = "1990-12-31T23:59:60Z";
        String noSubSecond_b2 = "1990-12-31T15:59:60-08:00";
        String noSubSecond_c = "2013-04-22T05:18:52Z";

        String noSubSecond_a1alt = "1996-12-19t16:39:57-08:00";
        String noSubSecond_a2alt = "1996-12-20t00:39:57Z";
        String noSubSecond_a3alt = "1996-12-20 00:39:57z";

        Date noSubSecond_a1d = RFC3339Util.rfc3339StringToDate(noSubSecond_a1);
        Date noSubSecond_a2d = RFC3339Util.rfc3339StringToDate(noSubSecond_a2);
        Date noSubSecond_a3d = RFC3339Util.rfc3339StringToDate(noSubSecond_a3);
        Date noSubSecond_a1altd = RFC3339Util.rfc3339StringToDate(noSubSecond_a1alt);
        Date noSubSecond_a2altd = RFC3339Util.rfc3339StringToDate(noSubSecond_a2alt);
        Date noSubSecond_a3altd = RFC3339Util.rfc3339StringToDate(noSubSecond_a3alt);
        Date noSubSecond_b1d = RFC3339Util.rfc3339StringToDate(noSubSecond_b1);
        Date noSubSecond_b2d = RFC3339Util.rfc3339StringToDate(noSubSecond_b2);
        Date noSubSecond_cd = RFC3339Util.rfc3339StringToDate(noSubSecond_c);

        String str_a1r =
                RFC3339Util.dateToRFC3339String(a1d, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);
        String str_a1r2 =
                RFC3339Util.dateToRFC3339String(a1d, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero).toLowerCase();
        String str_a1r3 =
                RFC3339Util.dateToRFC3339String(a1d, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero).replace('T', ' ');
        String str_a2r =
                RFC3339Util.dateToRFC3339String(a2d, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);
        String str_a3r =
                RFC3339Util.dateToRFC3339String(a3d, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);
        String str_b1r =
                RFC3339Util.dateToRFC3339String(b1d, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);
        String str_b2r =
                RFC3339Util.dateToRFC3339String(b2d, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);
        String str_cr = RFC3339Util.dateToRFC3339String(cd, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);
        String str_a1altr =
                RFC3339Util.dateToRFC3339String(a1altd, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);
        String str_a2altr =
                RFC3339Util.dateToRFC3339String(a2altd, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);
        String str_a3altr =
                RFC3339Util.dateToRFC3339String(a3altd, expiredate_forcezulu, true, expiredate_forceZinsteadOfZero);

        System.out.println("Reconstructed a1 str: " + str_a1r);
        System.out.println("Reconstructed a1 str: " + str_a1r2);
        System.out.println("Reconstructed a1 str: " + str_a1r3);

        Date a1dr = RFC3339Util.rfc3339StringToDate(str_a1r);
        Date a1dr2 = RFC3339Util.rfc3339StringToDate(str_a1r2);
        Date a1dr3 = RFC3339Util.rfc3339StringToDate(str_a1r3);
        Date a2dr = RFC3339Util.rfc3339StringToDate(str_a2r);
        Date a3dr = RFC3339Util.rfc3339StringToDate(str_a3r);
        Date b1dr = RFC3339Util.rfc3339StringToDate(str_b1r);
        Date b2dr = RFC3339Util.rfc3339StringToDate(str_b2r);
        Date cdr = RFC3339Util.rfc3339StringToDate(str_cr);
        Date a1altdr = RFC3339Util.rfc3339StringToDate(str_a1altr);
        Date a2altdr = RFC3339Util.rfc3339StringToDate(str_a2altr);
        Date a3altdr = RFC3339Util.rfc3339StringToDate(str_a3altr);

        Assert.assertEquals(noSubSecond_a1d, a1dr);
        Assert.assertEquals(noSubSecond_a1d, a1dr2);
        Assert.assertEquals(noSubSecond_a1d, a1dr3);
        Assert.assertEquals(noSubSecond_a2d, a2dr);
        Assert.assertEquals(noSubSecond_a3d, a3dr);
        Assert.assertEquals(noSubSecond_b1d, b1dr);
        Assert.assertEquals(noSubSecond_b2d, b2dr);
        Assert.assertEquals(noSubSecond_cd, cdr);
        Assert.assertEquals(noSubSecond_a1altd, a1altdr);
        Assert.assertEquals(noSubSecond_a2altd, a3altdr);
        Assert.assertEquals(noSubSecond_a3altd, a2altdr);
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////// Below: Test just the same (and more), but in another way //////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    @Test
    private void testDateA() throws ParseException {
        testDateStringRFC3339("1996-12-19T16:39:57.25-08:00");
    }

    @Test
    private void testDateB() throws ParseException {
        testDateStringRFC3339("1996-12-20T00:39:57.25Z");
    }

    @Test
    private void testDateC() throws ParseException {
        testDateStringRFC3339("1996-12-20T00:39:57.25+00:00");
    }

    @Test
    private void testDateD() throws ParseException {
        testDateStringRFC3339("1990-12-31T23:59:60Z");
    }

    @Test
    private void testDateE() throws ParseException {
        testDateStringRFC3339("1990-12-31T15:59:60-08:00");
    }

    @Test
    private void testDateF() throws ParseException {
        testDateStringRFC3339("2013-04-22T05:18:52Z");
    }

    @Test
    private void testDateG() throws ParseException {
        testDateStringRFC3339("1996-12-19t16:39:57.25-08:00");
    }

    @Test
    private void testDateH() throws ParseException {
        testDateStringRFC3339("1996-12-20t00:39:57.25Z");
    }

    @Test
    private void testDateI() throws ParseException {
        testDateStringRFC3339("1996-12-20 00:39:57.25z");
    }

    @Test
    private void testDateJ() throws ParseException {
        testDateStringRFC3339("2013-11-30T12:23:31Z");
    }

    @Test
    private void testDateJJ() throws ParseException {
        testDate(new Date(1385729148000l));
    }

    @Test
    private void testDateJJJ() throws ParseException {
        testDate(new Date(1385729148123l));
    }

    private void testDateStringRFC3339(String str) throws ParseException {
        System.out.println();
        System.out.println("Testing date: \"" + str + "\"");
        Date d = RFC3339Util.rfc3339StringToDate(str);
        testDate(d);
    }

    private void testDate(Date d) throws ParseException {
        System.out.println("Testing date: " + d);
        //try to convert to all possible formats, and back again, and check times.

        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                for (int k = 0; k < 2; k++)
                    for (int l = 0; l < 3; l++)
                        for (int m = 0; m < 1; m++)
                            for (int n = 0; n < 3; n++) {
                                boolean expiredate_forcezulu = i == 0;
                                boolean discard_subsecond = j == 1;
                                boolean expiredate_forceZinsteadOfZero = k == 0;
                                boolean expiredate_forcelowercaseT = l == 1;
                                boolean expiredate_forceTspace = l == 2;
                                boolean expiredate_forcelowercaseZ = m == 1;

                                String str =
                                        RFC3339Util.dateToRFC3339String(d, expiredate_forcezulu, discard_subsecond, expiredate_forceZinsteadOfZero);
                                if (expiredate_forcelowercaseT)
                                    str = str.replace('T', 't');
                                if (expiredate_forceTspace)
                                    str = str.replace('T', ' ');
                                if (expiredate_forcelowercaseZ)
                                    str = str.replace('Z', 'z');

                                Date d_recon = RFC3339Util.rfc3339StringToDate(str);

                                System.out.println("     Converted to string: \"" + str + "\"   " + "i=" + i + " " + "j=" + j + " " + "k=" + k + " " + "l=" + l + " " + "m=" + m + " " + "n=" + n + " -> " + d_recon);


                                if (!discard_subsecond)
                                    Assert.assertEquals(d, d_recon);
                                else
                                    Assert.assertEquals(d.getTime() - d.getTime() % 1000, d_recon.getTime());
                            }
        System.out.println();
    }

}
