package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

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

func TestTruncate(t *testing.T) {
	tmpDir := t.TempDir()

	mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
	rulesJsonFp := "testData/app_over_size.log"

	tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)

	cpFileForTest(t, rulesJsonFp, tmpLf)

	lf := &LogFile{
		path: tmpLf,
	}

	lf.path = tmpLf
	// Continue from here
	isEmpty := isFileEmpty(t, tmpLf)
	assert.Equal(t, isEmpty, false)

	err := lf.Truncate()
	assert.Equal(t, err, nil)
	isEmpty = isFileEmpty(t, tmpLf)
	assert.Equal(t, isEmpty, true)
}

func TestMakeCopyTo(t *testing.T) {
	tmpDir := t.TempDir()

	mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
	rulesJsonFp := "testData/app_over_size.log"

	tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)

	cpFileForTest(t, rulesJsonFp, tmpLf)

	newLocation := fmt.Sprintf("%s/tmp/app.1.log", tmpDir)

	lf := &LogFile{
		path: tmpLf,
	}

	lf.path = tmpLf

	isEmpty := isFileEmpty(t, tmpLf)
	assert.Equal(t, isEmpty, false)

	err := lf.MakeCopyTo(newLocation)
	assert.Equal(t, err, nil)
	isEmpty = isFileEmpty(t, tmpLf)
	assert.Equal(t, isEmpty, false)
	ok := areFilesTheSame(t, lf.path, newLocation)
	assert.Equal(t, ok, true)
}

func TestCompressFile(t *testing.T) {
	tmpDir := t.TempDir()

	mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
	rulesJsonFp := "testData/app_over_size.log"

	tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)

	cpFileForTest(t, rulesJsonFp, tmpLf)

	expected := fmt.Sprintf("%s/tmp/app.log.gz", tmpDir)

	err := compressOldFile(tmpLf)
	assert.Equal(t, err, nil)
	exists := doesFileExist(tmpLf)
	assert.Equal(t, exists, false)
	exists = doesFileExist(expected)
	assert.Equal(t, exists, true)
}

func TestRotate(t *testing.T) {
	tmpDir := t.TempDir()

	mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
	rulesJsonFp := "testData/app_over_size.log"

	tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)

	cpFileForTest(t, rulesJsonFp, tmpLf)

	lf := &LogFile{
		path:        tmpLf,
		canCompress: true,
	}

	fd, _ := os.Open(tmpLf)
	lf.handle = fd

	isEmpty := isFileEmpty(t, tmpLf)
	assert.Equal(t, isEmpty, false)
	err := lf.Rotate()
	assert.Equal(t, err, nil)
	exists := doesFileExist(tmpLf)
	assert.Equal(t, exists, true)
	isEmpty = isFileEmpty(t, tmpLf)
	assert.Equal(t, isEmpty, true)

	expected := walkMatch(t, tmpDir, "*.gz")
	assert.NotEmpty(t, expected)
	t.Logf("%+v", expected)

	assert.NotEqual(t, lf.handle, fd)
}

func TestNewLogger(t *testing.T) {
	t.Run("load logging file - do not Rotate", func(t *testing.T) {

		tmpDir := t.TempDir()

		mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
		rulesJsonFp := "testData/app_under_size.log"

		tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)

		cpFileForTest(t, rulesJsonFp, tmpLf)

		lf, err := newFileLogger(tmpLf, "6KB", true)
		expected := walkMatch(t, tmpDir, "*.gz")
		assert.Equal(t, err, nil)
		assert.Empty(t, expected)
		assert.Equal(t, lf.path, tmpLf)
		assert.NotEmpty(t, lf.handle)
		assert.NotEmpty(t, lf.logger)

		assert.FileExists(t, tmpLf)
		isEmpty := isFileEmpty(t, tmpLf)
		assert.Equal(t, isEmpty, false)
	})

	t.Run("load logging file - Rotate", func(t *testing.T) {
		tmpDir := t.TempDir()

		mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
		rulesJsonFp := "testData/app_over_size.log"

		tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)
		cpFileForTest(t, rulesJsonFp, tmpLf)

		lf, err := newFileLogger(tmpLf, "4KB", true)
		expected := walkMatch(t, tmpDir, "*.gz")
		assert.Equal(t, err, nil)
		assert.NotEmpty(t, expected)
		assert.Equal(t, lf.path, tmpLf)
		assert.NotEmpty(t, lf.handle)
		assert.NotEmpty(t, lf.logger)

		assert.FileExists(t, tmpLf)
		isEmpty := isFileEmpty(t, tmpLf)
		assert.Equal(t, isEmpty, true)
	})

	t.Run("create new logging file", func(t *testing.T) {
		tmpDir := t.TempDir()

		tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)

		lf, err := newFileLogger(tmpLf, "4KB", true)

		expected := walkMatch(t, tmpDir, "*.gz")
		assert.Equal(t, err, nil)
		assert.Empty(t, expected)
		assert.Equal(t, lf.path, tmpLf)
		assert.NotEmpty(t, lf.handle)
		assert.NotEmpty(t, lf.logger)

		assert.FileExists(t, tmpLf)
		isEmpty := isFileEmpty(t, tmpLf)
		assert.Equal(t, isEmpty, true)
	})
}

func TestWriteLog(t *testing.T) {
	t.Run("write to logging file - do not Rotate", func(t *testing.T) {
		req := httptest.NewRequest(http.MethodGet, "/{package}?go-get=1", nil)

		tmpDir := t.TempDir()

		tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)

		lf, err := newFileLogger(tmpLf, "4KB", true)
		assert.Equal(t, err, nil)
		assert.FileExists(t, tmpLf)
		isEmpty := isFileEmpty(t, tmpLf)
		assert.Equal(t, isEmpty, true)

		err = lf.WriteLog(req)
		assert.Equal(t, err, nil)

		expected := walkMatch(t, tmpDir, "*.gz")
		assert.Empty(t, expected)

		isEmpty = isFileEmpty(t, tmpLf)
		assert.Equal(t, isEmpty, false)

		b := readTestFile(t, tmpLf)
		m := make(map[string]string)
		_ = json.Unmarshal(b, &m)
		keys := make([]string, 0, len(m))
		for k := range m {
			keys = append(keys, k)
		}
		assert.Contains(t, keys, "requestUri")
		assert.Contains(t, keys, "Host")
		assert.Contains(t, keys, "method")
		assert.Contains(t, keys, "ipAddr")
		assert.Contains(t, keys, "requestDate")
	})

	t.Run("write to logging file - Rotate", func(t *testing.T) {
		req := httptest.NewRequest(http.MethodGet, "/{package}?go-get=1", nil)

		tmpDir := t.TempDir()

		mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
		rulesJsonFp := "testData/app_under_size.log"

		tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)
		cpFileForTest(t, rulesJsonFp, tmpLf)

		lf, err := newFileLogger(tmpLf, "5KB", true)
		assert.Equal(t, err, nil)
		assert.FileExists(t, tmpLf)
		isEmpty := isFileEmpty(t, tmpLf)
		assert.Equal(t, isEmpty, false)

		err = lf.WriteLog(req)
		assert.Equal(t, err, nil)
		err = lf.WriteLog(req)
		assert.Equal(t, err, nil)
		// t.Logf("%s\n", lf.curSize)

		expected := walkMatch(t, tmpDir, "*.gz")
		assert.NotEmpty(t, expected)

		assert.FileExists(t, tmpLf)
		isEmpty = isFileEmpty(t, tmpLf)
		assert.Equal(t, isEmpty, true)

	})
}