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

package jobqueue

import (
	"encoding/json"
	"github.com/fsnotify/fsnotify"
	"strings"
	"time"
)

type StopChan chan bool

type Scheduler interface {
	Schedule(job GenericJob, eventBus *EventBus) error
	Cancel(id JobID) error
	CancelAll() error
	JobExists(id JobID) bool
	GetType() string
	IsAdHoc() bool
	GetPersistence() SchedulerPersistence
}

type SchedulerPersistence struct {
	Time       *time.Time    `yaml:"time,omitempty" json:"time,omitempty" gorm:"column:time"`
	Interval   time.Duration `yaml:"interval,omitempty" json:"interval,omitempty" gorm:"column:interval"`
	Delay      time.Duration `yaml:"delay,omitempty" json:"delay,omitempty" gorm:"column:delay"`
	EventFlags fsnotify.Op   `yaml:"eventFlags,omitempty" json:"eventFlags,omitempty" gorm:"column:eventFlags"`

	Type     string    `yaml:"type" json:"type" gorm:"column:type"`
	Spec     string    `yaml:"spec,omitempty" json:"spec,omitempty" gorm:"column:spec"`
	Event    EventName `yaml:"event,omitempty" json:"event,omitempty" gorm:"column:event"`
	Executed bool      `yaml:"executed,omitempty" json:"executed,omitempty" gorm:"column:executed"`
	Path     string    `yaml:"path,omitempty" json:"path,omitempty" gorm:"column:path"`
}

type scheduleImportStruct struct {
	Time       *string `yaml:"time,omitempty" json:"time,omitempty"`
	Interval   *string `yaml:"interval,omitempty" json:"interval,omitempty"`
	Delay      *string `yaml:"delay,omitempty" json:"delay,omitempty"`
	EventFlags *string `yaml:"eventFlags,omitempty" json:"eventFlags,omitempty"`

	Type     string `yaml:"type" json:"type"`
	Spec     string `yaml:"spec,omitempty" json:"spec,omitempty"`
	Event    string `yaml:"event,omitempty" json:"event,omitempty"`
	Executed bool   `yaml:"executed,omitempty" json:"executed,omitempty"`
	Path     string `yaml:"path,omitempty" json:"path,omitempty"`
}

func (sp *SchedulerPersistence) parseAndAssignFields(aux scheduleImportStruct) error {

	sp.Type = aux.Type
	sp.Spec = aux.Spec
	sp.Event = EventName(aux.Event)
	sp.Executed = aux.Executed
	sp.Path = aux.Path

	if aux.Time != nil && *aux.Time != "" {
		var t time.Time
		var err error
		for _, format := range SupportedTimeFormats {
			t, err = time.Parse(format, *aux.Time)
			if err == nil {
				break
			}
		}
		if err != nil {
			return err
		}
		sp.Time = &t
	}

	if aux.Interval != nil && *aux.Interval != "" {
		d, err := time.ParseDuration(*aux.Interval)
		if err != nil {
			return err
		}
		sp.Interval = d
	}

	if aux.Delay != nil && *aux.Delay != "" {
		d, err := time.ParseDuration(*aux.Delay)
		if err != nil {
			return err
		}
		sp.Delay = d
	}

	if aux.EventFlags != nil && *aux.EventFlags != "" {
		sp.EventFlags = fsnotify.Op(0)
		for _, flag := range strings.Split(*aux.EventFlags, "|") {
			switch flag {
			case "Create":
				sp.EventFlags |= fsnotify.Create
			case "Write":
				sp.EventFlags |= fsnotify.Write
			case "Remove":
				sp.EventFlags |= fsnotify.Remove
			case "Rename":
				sp.EventFlags |= fsnotify.Rename
			case "Chmod":
				sp.EventFlags |= fsnotify.Chmod
			}
		}
	}

	return nil
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (sp *SchedulerPersistence) UnmarshalJSON(data []byte) error {
	var aux scheduleImportStruct
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	return sp.parseAndAssignFields(aux)
}

func (sp *SchedulerPersistence) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var aux scheduleImportStruct
	if err := unmarshal(&aux); err != nil {
		return err
	}
	return sp.parseAndAssignFields(aux)
}