package monitor

import (
	"os"
	"path"
	"reflect"
	"regexp"
	"sync"
	"testing"
	"time"
)

func TestLogMonitor_Watch(t *testing.T) {
	type args struct {
		filter *regexp.Regexp
	}
	type data struct {
		lines []string
	}
	tests := []struct {
		name string
		args args
		data data
		want []string
	}{
		{
			name: "watch all lines",
			args: args{
				regexp.MustCompile(".*"),
			},
			data: data{lines: []string{"line1", "line2"}},
			want: []string{"line1", "line2"},
		},
		{
			name: "match none lines",
			args: args{
				regexp.MustCompile(".*with unknown pattern.*"),
			},
			data: data{lines: []string{"line1", "line2"}},
			want: []string{},
		},
		{
			name: "match one line",
			args: args{
				regexp.MustCompile(".* to keep$"),
			},
			data: data{lines: []string{"line1", "line2 to keep"}},
			want: []string{"line2 to keep"},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dir, err := os.MkdirTemp("", "watch")
			logfile := path.Join(dir, "file.log")
			if err != nil {
				t.Errorf("unable to init tmp dir: %v", err)
				return
			}
			defer os.RemoveAll(dir)

			l := New()
			defer l.Close()

			chanLines := make(chan string, 5)
			go func() {
				err := l.Watch(logfile, tt.args.filter, chanLines)
				if err != nil {
					t.Errorf("unable to start to watching logs: %v", err)
				}
			}()

			// Create new file to watch
			file, err := os.Create(logfile)
			if err != nil {
				t.Errorf("unable to create log file: %v", err)
			}
			defer file.Close()

			// Write test data into watched file
			waitEndWriting := sync.WaitGroup{}
			waitEndWriting.Add(len(tt.data.lines))
			go func() {
				for _, line := range tt.data.lines {
					_, err = file.WriteString(line + "\n")
					if err != nil {
						t.Errorf("unable to write line '%v': %v", line, err)
						return
					}
					waitEndWriting.Done()
				}
			}()

			lineCpt := 0
			waitEndWriting.Wait()
			got := make([]string, 0, len(tt.want))
			for len(got) < len(tt.want) {
				select {
				case line := <-chanLines:
					got = append(got, line)
					lineCpt++
				case <-time.After(1 * time.Millisecond):
					t.Errorf("waiting line timeout")
					t.FailNow()
				}
			}

			if !reflect.DeepEqual(tt.want, got) {
				t.Errorf("LogMonitor_Watch(), bad lines published: got = %v, want %v", got, tt.want)
			}

		})
	}
}
