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

package jobqueue

import (
	"encoding/json"
	"github.com/robfig/cron/v3"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestCreateJobAndSchedulerFromInput(t *testing.T) {
	tests := []struct {
		name      string
		input     JobPersistence
		wantJob   GenericJob
		wantSched Scheduler
		wantErr   bool
	}{
		{
			name: "Shell Runnable and Interval Scheduler",
			input: JobPersistence{
				ID:         "1",
				Priority:   1,
				Timeout:    10 * time.Second,
				MaxRetries: 3,
				RetryDelay: 2 * time.Second,
				Runnable: RunnableImport{
					Type: "Shell",
					Data: map[string]any{"ScriptPath": "script.sh"},
				},
				Scheduler: SchedulerPersistence{
					Type:     "Interval",
					Interval: 1 * time.Minute,
				},
			},
			wantJob:   GenericJob(&Job[ShellResult]{ /* Initialization */ }),
			wantSched: &IntervalScheduler{Interval: 1 * time.Minute},
			wantErr:   false,
		},
		{
			name: "Shell Runnable and Cron Scheduler",
			input: JobPersistence{
				ID:       "1",
				Priority: 1,
				Runnable: RunnableImport{
					Type: "Shell",
					Data: map[string]any{"ScriptPath": "script.sh"},
				},
				Scheduler: SchedulerPersistence{
					Type: "Cron",
					Spec: "* * * * * *",
				},
			},
			wantJob:   GenericJob(&Job[ShellResult]{ /* Initialization */ }),
			wantSched: &CronScheduler{ /* Initialization */ },
			wantErr:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotJob, gotSchedule, err := CreateJobAndSchedulerFromPersistence(tt.input, nil)
			if gotSchedule != nil {
				if gotSchedule.GetType() == "Cron" {
					cronInst := cron.New(cron.WithSeconds())
					gotSchedule.(*CronScheduler).cron = cronInst
					cronInst.Start()
					defer cronInst.Stop()

				}
			}

			if (err != nil) != tt.wantErr {
				t.Errorf("CreateJobAndSchedulerFromPersistence() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			assert.Equal(t, JobID(tt.input.ID), gotJob.GetID(), "Job ID mismatch")
			assert.Equal(t, Priority(tt.input.Priority), gotJob.GetPriority(), "Job Priority mismatch")

			assert.Equal(t, tt.input.Scheduler.Type, gotSchedule.GetType(), "Scheduler Type mismatch")

		})
	}
}

func TestReadJsonFile(t *testing.T) {
	testContent := `[{"id": "1", "priority": 1}]`
	tempFile, err := ioutil.TempFile("", "test.json")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tempFile.Name())

	if _, err := tempFile.Write([]byte(testContent)); err != nil {
		t.Fatal(err)
	}
	tempFile.Close()

	jobs, err := ReadJsonFile(tempFile.Name())
	if err != nil {
		t.Fatal(err)
	}

	if len(jobs) != 1 || jobs[0].ID != "1" || jobs[0].Priority != 1 {
		t.Errorf("Expected job with ID '1' and priority 1, got %+v", jobs)
	}
}

func TestReadYAMLFile(t *testing.T) {
	testContent := `- id: "1"
  priority: 1
`
	tempFile, err := ioutil.TempFile("", "test.yaml")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tempFile.Name())

	if _, err := tempFile.Write([]byte(testContent)); err != nil {
		t.Fatal(err)
	}
	tempFile.Close()

	jobs, err := ReadYAMLFile(tempFile.Name())
	if err != nil {
		t.Fatal(err)
	}

	if len(jobs) != 1 || jobs[0].ID != "1" || jobs[0].Priority != 1 {
		t.Errorf("Expected job with ID '1' and priority 1, got %+v", jobs)
	}
}

func TestJobPersistenceUnmarshalJSON(t *testing.T) {
	testCases := []struct {
		name      string
		jsonInput string
		expected  *time.Time
		wantErr   bool
	}{
		{
			name:      "RFC3339 Format",
			jsonInput: `{"pauseUntil":"2023-11-15T09:01:00Z"}`,
			expected:  parseTimeForTesting(t, "2023-11-15T09:01:00Z"),
			wantErr:   false,
		},
		{
			name:      "Date and Time",
			jsonInput: `{"pauseUntil":"2023-11-15T09:01"}`,
			expected:  parseTimeForTesting(t, "2023-11-15T09:01"),
			wantErr:   false,
		},
		{
			name:      "Only Date",
			jsonInput: `{"pauseUntil":"2023-11-15"}`,
			expected:  parseTimeForTesting(t, "2023-11-15"),
			wantErr:   false,
		},
		{
			name:      "Invalid Format",
			jsonInput: `{"pauseUntil":"15. November 2023"}`,
			expected:  nil,
			wantErr:   true,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			var jp JobPersistence
			err := json.Unmarshal([]byte(tc.jsonInput), &jp)

			if tc.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Equal(t, tc.expected, jp.PauseUntil)
			}
		})
	}
}