package autobuild

import (
	"database/sql"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/coder/coder/v2/coderd/database"
	"github.com/coder/coder/v2/coderd/schedule"
)

func Test_isEligibleForAutostart(t *testing.T) {
	t.Parallel()

	// okXXX should be set to values that make 'isEligibleForAutostart' return true.

	// Intentionally chosen to be a non UTC time that changes the day of the week
	// when converted to UTC.
	localLocation, err := time.LoadLocation("America/Chicago")
	if err != nil {
		t.Fatal(err)
	}

	// 5s after the autostart in UTC.
	okTick := time.Date(2021, 1, 1, 20, 0, 5, 0, localLocation).UTC()
	okUser := database.User{Status: database.UserStatusActive}
	okWorkspace := database.Workspace{
		DormantAt: sql.NullTime{Valid: false},
		AutostartSchedule: sql.NullString{
			Valid: true,
			// Every day at 8pm America/Chicago, which is 2am UTC the next day.
			String: "CRON_TZ=America/Chicago 0 20 * * *",
		},
	}
	okBuild := database.WorkspaceBuild{
		Transition: database.WorkspaceTransitionStop,
		// Put 24hr before the tick so it's eligible for autostart.
		CreatedAt: okTick.Add(time.Hour * -24),
	}
	okJob := database.ProvisionerJob{
		JobStatus: database.ProvisionerJobStatusSucceeded,
	}
	okTemplateSchedule := schedule.TemplateScheduleOptions{
		UserAutostartEnabled: true,
		AutostartRequirement: schedule.TemplateAutostartRequirement{
			DaysOfWeek: 0b01111111,
		},
	}
	var okWeekdayBit uint8
	for i, weekday := range schedule.DaysOfWeek {
		// Find the local weekday
		if okTick.In(localLocation).Weekday() == weekday {
			okWeekdayBit = 1 << uint(i)
		}
	}

	testCases := []struct {
		Name             string
		User             database.User
		Workspace        database.Workspace
		Build            database.WorkspaceBuild
		Job              database.ProvisionerJob
		TemplateSchedule schedule.TemplateScheduleOptions
		Tick             time.Time

		ExpectedResponse bool
	}{
		{
			Name:             "Ok",
			User:             okUser,
			Workspace:        okWorkspace,
			Build:            okBuild,
			Job:              okJob,
			TemplateSchedule: okTemplateSchedule,
			Tick:             okTick,
			ExpectedResponse: true,
		},
		{
			Name:             "SuspendedUser",
			User:             database.User{Status: database.UserStatusSuspended},
			Workspace:        okWorkspace,
			Build:            okBuild,
			Job:              okJob,
			TemplateSchedule: okTemplateSchedule,
			Tick:             okTick,
			ExpectedResponse: false,
		},
		{
			Name:      "AutostartOnlyDayEnabled",
			User:      okUser,
			Workspace: okWorkspace,
			Build:     okBuild,
			Job:       okJob,
			TemplateSchedule: schedule.TemplateScheduleOptions{
				UserAutostartEnabled: true,
				AutostartRequirement: schedule.TemplateAutostartRequirement{
					// Specific day of week is allowed
					DaysOfWeek: okWeekdayBit,
				},
			},
			Tick:             okTick,
			ExpectedResponse: true,
		},
		{
			Name:      "AutostartOnlyDayDisabled",
			User:      okUser,
			Workspace: okWorkspace,
			Build:     okBuild,
			Job:       okJob,
			TemplateSchedule: schedule.TemplateScheduleOptions{
				UserAutostartEnabled: true,
				AutostartRequirement: schedule.TemplateAutostartRequirement{
					// Specific day of week is disallowed
					DaysOfWeek: 0b01111111 & (^okWeekdayBit),
				},
			},
			Tick:             okTick,
			ExpectedResponse: false,
		},
		{
			Name:      "AutostartAllDaysDisabled",
			User:      okUser,
			Workspace: okWorkspace,
			Build:     okBuild,
			Job:       okJob,
			TemplateSchedule: schedule.TemplateScheduleOptions{
				UserAutostartEnabled: true,
				AutostartRequirement: schedule.TemplateAutostartRequirement{
					// All days disabled
					DaysOfWeek: 0,
				},
			},
			Tick:             okTick,
			ExpectedResponse: false,
		},
		{
			Name:      "BuildTransitionNotStop",
			User:      okUser,
			Workspace: okWorkspace,
			Build: func(b database.WorkspaceBuild) database.WorkspaceBuild {
				cpy := b
				cpy.Transition = database.WorkspaceTransitionStart
				return cpy
			}(okBuild),
			Job:              okJob,
			TemplateSchedule: okTemplateSchedule,
			Tick:             okTick,
			ExpectedResponse: false,
		},
	}

	for _, c := range testCases {
		c := c
		t.Run(c.Name, func(t *testing.T) {
			t.Parallel()

			autostart := isEligibleForAutostart(c.User, c.Workspace, c.Build, c.Job, c.TemplateSchedule, c.Tick)
			require.Equal(t, c.ExpectedResponse, autostart, "autostart not expected")
		})
	}
}
