// go.stargrave.org/tai64n -- Pure Go TAI64/TAI64N implementation
// Copyright (C) 2020-2026 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package tai64n

import (
	"testing"
	"testing/quick"
	"time"
)

func TestVectors(t *testing.T) {
	tm, err := Decode("400000002a2b2c2d")
	if err != nil {
		t.Fatal(err)
	}
	ref := time.Date(1992, 6, 2, 8, 7, 9, 0, time.UTC)
	if !tm.Equal(ref) {
		t.Fatal("TAI64 != reference")
	}
	tm, isLeap := Leapsecs.Sub(tm)
	ref = time.Date(1992, 6, 2, 8, 6, 43, 0, time.UTC)
	if !tm.Equal(ref) {
		t.Fatal("UTC != reference")
	}
	if isLeap {
		t.Fatal("unexpectedly leap")
	}

	tm, err = Decode("4000000034353637")
	if err != nil {
		t.Fatal(err)
	}
	ref = time.Date(1997, 10, 3, 18, 15, 19, 0, time.UTC)
	if !tm.Equal(ref) {
		t.Fatal("TAI64 != reference")
	}
	tm, isLeap = Leapsecs.Sub(tm)
	ref = time.Date(1997, 10, 3, 18, 14, 48, 0, time.UTC)
	if !tm.Equal(ref) {
		t.Fatal("UTC != reference")
	}
	if isLeap {
		t.Fatal("unexpectedly leap")
	}

	tm, err = Decode("40000000586846a3")
	if err != nil {
		t.Fatal(err)
	}
	tm, isLeap = Leapsecs.Sub(tm)
	ref = time.Date(2016, 12, 31, 23, 59, 59, 0, time.UTC)
	if !tm.Equal(ref) {
		t.Fatal("UTC != reference")
	}
	if isLeap {
		t.Fatal("unexpectedly leap")
	}

	tm, err = Decode("40000000586846a4")
	if err != nil {
		t.Fatal(err)
	}
	tm, isLeap = Leapsecs.Sub(tm)
	ref = time.Date(2016, 12, 31, 23, 59, 59, 0, time.UTC)
	if !tm.Equal(ref) {
		t.Fatal("UTC != reference")
	}
	if !isLeap {
		t.Fatal("unexpectedly non-leap")
	}

	tm, err = Decode("40000000586846a5")
	if err != nil {
		t.Fatal(err)
	}
	tm, isLeap = Leapsecs.Sub(tm)
	ref = time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)
	if !tm.Equal(ref) {
		t.Fatal("UTC != reference")
	}
	if isLeap {
		t.Fatal("unexpectedly leap")
	}

	tm, err = Decode("40000000586846a6")
	if err != nil {
		t.Fatal(err)
	}
	tm, isLeap = Leapsecs.Sub(tm)
	ref = time.Date(2017, 1, 1, 0, 0, 1, 0, time.UTC)
	if !tm.Equal(ref) {
		t.Fatal("UTC != reference")
	}
	if isLeap {
		t.Fatal("unexpectedly leap")
	}
}

func TestLeaped(t *testing.T) {
	tm := time.Date(2016, 12, 31, 23, 59, 59, 0, time.UTC)
	tm = Leapsecs.Add(tm)
	var tai TAI64
	tai.FromTime(tm)
	if tai.Encode() != "@40000000586846a3" {
		t.Fatal("bad 2016-12-31 23:59:59")
	}

	tm = time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)
	tm = Leapsecs.Add(tm)
	tai.FromTime(tm)
	if tai.Encode() != "@40000000586846a5" {
		t.Fatal("bad 2017-01-01 00:00:00")
	}

	tm = time.Date(2017, 1, 1, 0, 0, 1, 0, time.UTC)
	tm = Leapsecs.Add(tm)
	tai.FromTime(tm)
	if tai.Encode() != "@40000000586846a6" {
		t.Fatal("bad 2017-01-01 00:00:01")
	}
}

func TestLeapsecsSymmetric(t *testing.T) {
	f := func(secs int64) bool {
		tm := time.Unix(secs, 0)
		got, isLeap := Leapsecs.Sub(Leapsecs.Add(tm))
		if isLeap {
			return false
		}
		return tm == got
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func Test1969(t *testing.T) {
	tm := time.Date(1969, 1, 1, 0, 0, 0, 0, time.UTC)
	tm = Leapsecs.Add(tm)
	var tai TAI64
	tai.FromTime(tm)
	tm = tai.Time()
	tm, _ = Leapsecs.Sub(tm)
	if tm != time.Date(1969, 1, 1, 0, 0, 0, 0, time.UTC) {
		t.Fatal("bad 1969 without ms")
	}

	tm = time.Date(1969, 1, 1, 0, 0, 0, 1, time.UTC)
	tm = Leapsecs.Add(tm)
	var tain TAI64N
	tain.FromTime(tm)
	tm = tain.Time()
	tm, _ = Leapsecs.Sub(tm)
	if tm != time.Date(1969, 1, 1, 0, 0, 0, 1, time.UTC) {
		t.Fatal("bad 1969 with ms=1")
	}
}

func BenchmarkTAI64(b *testing.B) {
	now := time.Now()
	now = now.Truncate(time.Second)
	var tai TAI64
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tai.FromTime(now)
		if !tai.Time().Equal(now) {
			b.FailNow()
		}
	}
}

func BenchmarkTAI64N(b *testing.B) {
	now := time.Now()
	var tai TAI64N
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tai.FromTime(now)
		if !tai.Time().Equal(now) {
			b.FailNow()
		}
	}
}
