package watch

import (
	"errors"
	"os"
	"testing"

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

func TestAddWatch(t *testing.T) {
	tempDir, err := os.MkdirTemp("", "watchtest")
	assert.Nil(t, err)
	defer func() {
		_ = os.RemoveAll(tempDir) // Cleanup
	}()

	l := NewLighthouse()

	w := &Watch{Path: tempDir}
	err = l.Add(w)
	assert.Nil(t, err)

	err = l.Add(w)
	assert.True(t, errors.As(err, &AlreadyWatchedPathError{}))
}

func TestRemoveWatch(t *testing.T) {
	tempDir, err := os.MkdirTemp("", "watchtest")
	assert.Nil(t, err)
	defer func() {
		_ = os.RemoveAll(tempDir) // Cleanup
	}()

	l := NewLighthouse()

	w := &Watch{Path: tempDir}
	err = l.Add(w)
	assert.Nil(t, err)

	err = l.Remove(tempDir)
	assert.Nil(t, err)

	err = l.Remove("path/not/watched")
	assert.True(t, errors.As(err, &UnwatchedPathError{}))
}

func TestNewLighthouse(t *testing.T) {
	l := NewLighthouse()
	assert.NotNil(t, l)
	assert.True(t, l.IsActive())
}

func TestIsWatched(t *testing.T) {
	tempDir, err := os.MkdirTemp("", "watchtest")
	assert.Nil(t, err)
	defer func() {
		_ = os.RemoveAll(tempDir) // Cleanup
	}()

	l := NewLighthouse()

	w := &Watch{Path: tempDir}
	err = l.Add(w)
	assert.Nil(t, err)

	assert.True(t, l.IsWatched(tempDir))
	assert.False(t, l.IsWatched("path/not/watched"))
}

func TestIsActiveWatched(t *testing.T) {
	tempDir, err := os.MkdirTemp("", "watchtest")
	assert.Nil(t, err)
	defer func() {
		_ = os.RemoveAll(tempDir) // Cleanup
	}()

	l := NewLighthouse()

	w := &Watch{Path: tempDir}
	err = l.Add(w)
	assert.Nil(t, err)

	assert.True(t, l.IsActiveWatched(tempDir))
	assert.False(t, l.IsActiveWatched("path/not/watched"))
}

func TestWatchList(t *testing.T) {
	tempDir, err := os.MkdirTemp("", "watchtest")
	assert.Nil(t, err)
	defer func() {
		_ = os.RemoveAll(tempDir) // Cleanup
	}()

	l := NewLighthouse()

	w := &Watch{Path: tempDir}
	err = l.Add(w)
	assert.Nil(t, err)

	assert.Equal(t, 1, len(l.WatchList()))
	assert.Equal(t, tempDir, l.WatchList()[0])
}

// Test IsInSync with a file that is not watched by fsnotify
func TestIsInSync(t *testing.T) {
	tempDir, err := os.MkdirTemp("", "watchtest")
	assert.Nil(t, err)

	tempFile, err := os.CreateTemp(tempDir, "test")
	assert.Nil(t, err)

	defer func() {
		_ = os.RemoveAll(tempDir)         // Cleanup
		_ = os.RemoveAll(tempFile.Name()) // Cleanup
	}()

	l := NewLighthouse()

	w := &Watch{Path: tempDir}
	err = l.Add(w)
	assert.Nil(t, err)

	w2 := &Watch{Path: tempFile.Name()}
	err = l.Add(w2)
	assert.Nil(t, err)

	err = l.Sync()
	assert.Nil(t, err)

	assert.True(t, l.IsInSync())

	internal := l.(*lighthouse)
	internal.fsnotify.Remove(tempFile.Name())

	assert.False(t, l.IsInSync())

}

// test Sync
func TestSync(t *testing.T) {
	tempDir, err := os.MkdirTemp("", "watchtest")
	assert.Nil(t, err)

	tempFile, err := os.CreateTemp(tempDir, "test")
	assert.Nil(t, err)

	defer func() {
		_ = os.RemoveAll(tempDir)         // Cleanup
		_ = os.RemoveAll(tempFile.Name()) // Cleanup
	}()

	l := NewLighthouse()

	w := &Watch{Path: tempDir}
	err = l.Add(w)
	assert.Nil(t, err)

	w2 := &Watch{Path: tempFile.Name()}
	err = l.Add(w2)
	assert.Nil(t, err)

	err = l.Sync()
	assert.Nil(t, err)
}