package helpers

import (
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const fileArchiverUntrackedFile = "untracked_test_file.txt"
const fileArchiverUntrackedUnicodeFile = "неотслеживаемый_тестовый_файл.txt"
const fileArchiverArchiveZipFile = "archive.zip"
const fileArchiverNotExistingFile = "not_existing_file.txt"
const fileArchiverAbsoluteFile = "/absolute.txt"
const fileArchiverRelativeFile = "../../../relative.txt"

func TestCacheArchiverAddingUntrackedFiles(t *testing.T) {
	writeTestFile(t, artifactsTestArchivedFile)
	defer os.Remove(artifactsTestArchivedFile)

	writeTestFile(t, artifactsTestArchivedFile2)
	defer os.Remove(artifactsTestArchivedFile2)

	f := fileArchiver{
		Untracked: true,
	}
	err := f.enumerate()
	assert.NoError(t, err)
	assert.Len(t, f.sortedFiles(), 2)
	assert.Contains(t, f.sortedFiles(), artifactsTestArchivedFile)
	assert.Contains(t, f.sortedFiles(), artifactsTestArchivedFile2)
}

func TestCacheArchiverAddingUntrackedUnicodeFiles(t *testing.T) {
	writeTestFile(t, fileArchiverUntrackedUnicodeFile)
	defer os.Remove(fileArchiverUntrackedUnicodeFile)

	f := fileArchiver{
		Untracked: true,
	}
	err := f.enumerate()
	assert.NoError(t, err)
	assert.Len(t, f.sortedFiles(), 1)
	assert.Contains(t, f.sortedFiles(), fileArchiverUntrackedUnicodeFile)
}

func TestCacheArchiverAddingFile(t *testing.T) {
	writeTestFile(t, fileArchiverUntrackedFile)
	defer os.Remove(fileArchiverUntrackedFile)

	f := fileArchiver{
		Paths: []string{fileArchiverUntrackedFile},
	}
	err := f.enumerate()
	assert.NoError(t, err)
	assert.Len(t, f.sortedFiles(), 1)
	assert.Contains(t, f.sortedFiles(), fileArchiverUntrackedFile)
}

func TestFileArchiverToFailOnAbsoluteFile(t *testing.T) {
	f := fileArchiver{
		Paths: []string{fileArchiverAbsoluteFile},
	}
	err := f.enumerate()
	assert.NoError(t, err)
	assert.Empty(t, f.sortedFiles())
	assert.NotContains(t, f.sortedFiles(), fileArchiverAbsoluteFile)
}

func TestFileArchiverToFailOnRelativeFile(t *testing.T) {
	f := fileArchiver{
		Paths: []string{fileArchiverRelativeFile},
	}
	err := f.enumerate()
	assert.NoError(t, err)
	assert.Empty(t, f.sortedFiles())
}

func TestFileArchiverToAddNotExistingFile(t *testing.T) {
	f := fileArchiver{
		Paths: []string{fileArchiverNotExistingFile},
	}
	err := f.enumerate()
	assert.NoError(t, err)
	assert.Empty(t, f.sortedFiles())
}

func TestFileArchiverChanged(t *testing.T) {
	writeTestFile(t, fileArchiverUntrackedFile)
	defer os.Remove(fileArchiverUntrackedFile)

	now := time.Now()
	require.NoError(t, os.Chtimes(fileArchiverUntrackedFile, now, now.Add(-time.Second)))

	f := fileArchiver{
		Paths: []string{fileArchiverUntrackedFile},
	}
	err := f.enumerate()
	require.NoError(t, err)
	assert.Len(t, f.sortedFiles(), 1)
	assert.False(t, f.isChanged(now.Add(time.Minute)))
	assert.True(t, f.isChanged(now.Add(-time.Minute)))
}

func TestFileArchiverFileIsNotChanged(t *testing.T) {
	now := time.Now()

	writeTestFile(t, fileArchiverUntrackedFile)
	defer os.Remove(fileArchiverUntrackedFile)

	writeTestFile(t, fileArchiverArchiveZipFile)
	defer os.Remove(fileArchiverArchiveZipFile)

	f := fileArchiver{
		Paths: []string{fileArchiverUntrackedFile},
	}
	err := f.enumerate()
	require.NoError(t, err)

	require.NoError(t, os.Chtimes(fileArchiverUntrackedFile, now, now.Add(-time.Second)))
	assert.False(t, f.isFileChanged(fileArchiverArchiveZipFile), "should return false if file was modified before the listed file")
}

func TestFileArchiverFileIsChanged(t *testing.T) {
	now := time.Now()

	writeTestFile(t, fileArchiverUntrackedFile)
	defer os.Remove(fileArchiverUntrackedFile)

	writeTestFile(t, fileArchiverArchiveZipFile)
	defer os.Remove(fileArchiverArchiveZipFile)

	f := fileArchiver{
		Paths: []string{fileArchiverUntrackedFile},
	}
	err := f.enumerate()
	require.NoError(t, err)

	require.NoError(t, os.Chtimes(fileArchiverArchiveZipFile, now, now.Add(-time.Minute)))
	assert.True(t, f.isFileChanged(fileArchiverArchiveZipFile), "should return true if file was modified")
}

func TestFileArchiverFileDoesNotExist(t *testing.T) {
	writeTestFile(t, fileArchiverUntrackedFile)
	defer os.Remove(fileArchiverUntrackedFile)

	f := fileArchiver{
		Paths: []string{fileArchiverUntrackedFile},
	}
	err := f.enumerate()
	require.NoError(t, err)

	assert.True(t, f.isFileChanged(fileArchiverNotExistingFile), "should return true if file doesn't exist")
}
