// Copyright 2023 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0

package jobqueue

import (
	"context"
	"github.com/stretchr/testify/assert"
	"os"
	"path"
	"testing"
	"time"
)

func TestPauseJob(t *testing.T) {

	runner := &ShellRunnable{ScriptPath: "path"}
	job := NewJob[ShellResult]("id1", runner)

	job.Pause()
	assert.True(t, job.IsPaused())
	job.Resume()
	assert.False(t, job.IsPaused())
}

func TestNewJob(t *testing.T) {
	runner := &ShellRunnable{ScriptPath: "path"}
	job := NewJob[ShellResult]("id1", runner)
	assert.Equal(t, JobID("id1"), job.GetID())
	assert.Equal(t, PriorityDefault, job.GetPriority())
}

func TestSetPriority(t *testing.T) {
	job := NewJob[ShellResult]("id1", &ShellRunnable{})
	job.SetPriority(PriorityHigh)
	assert.Equal(t, PriorityHigh, job.GetPriority())
}

func TestSetAndGetTimeout(t *testing.T) {
	job := NewJob[ShellResult]("id1", &ShellRunnable{})
	job.SetTimeout(5 * time.Minute)
	assert.Equal(t, 5*time.Minute, *job.GetTimeout())
}

func TestSetAndGetMaxRetries(t *testing.T) {
	job := NewJob[ShellResult]("id1", &ShellRunnable{})
	job.SetMaxRetries(5)
	assert.Equal(t, uint(5), job.GetMaxRetries())
}

func TestSetAndGetRunnable(t *testing.T) {
	runner := &ShellRunnable{ScriptPath: "path"}
	job := NewJob[ShellResult]("id1", runner)
	assert.Equal(t, runner, job.GetRunnable())
}

func TestSetAndGetRetryDelay(t *testing.T) {
	job := NewJob[ShellResult]("id1", &ShellRunnable{})
	job.SetRetryDelay(2 * time.Second)
	assert.Equal(t, 2*time.Second, *job.GetRetryDelay())
}

func TestSetAndGetDependencies(t *testing.T) {
	job := NewJob[ShellResult]("id1", &ShellRunnable{})
	job.SetDependencies([]JobID{"id2", "id3"})
	assert.Equal(t, []JobID{"id2", "id3"}, job.GetDependencies())
}

type TestScheduler struct{}

func (s *TestScheduler) Schedule(job *GenericJob, eventBus *EventBus, stopChan chan bool) error {
	return nil
}

func TestGetID(t *testing.T) {
	job := NewJob[ShellResult]("id1", &ShellRunnable{})
	assert.Equal(t, JobID("id1"), job.GetID())
}

func TestGetRunnable(t *testing.T) {
	runner := &ShellRunnable{ScriptPath: "path"}
	job := NewJob[ShellResult]("id1", runner)
	assert.Equal(t, runner, job.GetRunnable())
}

func TestGetPriority(t *testing.T) {
	job := NewJob[ShellResult]("id2", &ShellRunnable{})
	assert.Equal(t, PriorityDefault, job.GetPriority())
}

func TestGetTimeout(t *testing.T) {
	job := NewJob[ShellResult]("id2", &ShellRunnable{})
	assert.Nil(t, job.GetTimeout())
}

func TestGetMaxRetries(t *testing.T) {
	job := NewJob[ShellResult]("id2", &ShellRunnable{})
	assert.Equal(t, uint(0), job.GetMaxRetries())
}

func TestGetRetryDelay(t *testing.T) {
	job := NewJob[ShellResult]("id2", &ShellRunnable{})
	assert.Nil(t, job.GetRetryDelay())
}

func TestGetDependencies(t *testing.T) {
	job := NewJob[ShellResult]("id2", &ShellRunnable{})
	assert.Equal(t, []JobID(nil), job.GetDependencies())
}

func TestSetDependencies(t *testing.T) {
	job := NewJob[ShellResult]("id1", &ShellRunnable{})
	job.SetDependencies([]JobID{"id2", "id3"})
	assert.Equal(t, []JobID{"id2", "id3"}, job.GetDependencies())
}

func TestJobStats(t *testing.T) {
	scriptContent := `#!/env/bin/bash 
echo "Hello World"
`

	tmpDir := t.TempDir()
	tmpFile := "example.sh"
	tmpPath := path.Join(tmpDir, tmpFile)

	tmpfile, err := os.Create(tmpPath)
	if err != nil {
		t.Fatal(err)
	}
	_, _ = tmpfile.Write([]byte(scriptContent))

	defer tmpfile.Close()

	job := NewJob[ShellResult]("id1", &ShellRunnable{ScriptPath: tmpfile.Name()})

	// Simulate a successful run
	result, err := job.Execute(context.Background()) // Assume Execute updates stats and returns no error
	assert.NoError(t, err)
	assert.NotNil(t, result)

	stats := job.stats

	assert.Equal(t, 1, stats.RunCount)
	assert.Equal(t, 1, stats.SuccessCount)
	assert.Equal(t, 0, stats.ErrorCount)
	assert.NotEqual(t, 0, stats.TimeMetrics.TotalRunTime)
	assert.NotEqual(t, 0, stats.TimeMetrics.AvgRunTime)
	assert.NotEqual(t, 0, stats.TimeMetrics.MaxRunTime)
	assert.NotEqual(t, 0, stats.TimeMetrics.MinRunTime)

	// Simulate a failed run
	_, _ = job.Execute(context.Background()) // Assume Execute updates stats and returns an error
	stats = job.stats

	assert.Equal(t, 2, stats.RunCount)
	assert.Equal(t, 2, stats.SuccessCount)
	assert.Equal(t, 0, stats.ErrorCount)
}