// Copyright (C) NHR@FAU, University Erlangen-Nuremberg.
// All rights reserved. This file is part of cc-backend.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repository

import (
	"sync"

	"github.com/ClusterCockpit/cc-lib/v2/schema"
)

// JobHook interface allows external components to hook into job lifecycle events.
// Implementations can perform actions when jobs start or stop, such as tagging,
// logging, notifications, or triggering external workflows.
//
// Example implementation:
//
//	type MyJobTagger struct{}
//
//	func (t *MyJobTagger) JobStartCallback(job *schema.Job) {
//	    if job.NumNodes > 100 {
//	        // Tag large jobs automatically
//	    }
//	}
//
//	func (t *MyJobTagger) JobStopCallback(job *schema.Job) {
//	    if job.State == schema.JobStateFailed {
//	        // Log or alert on failed jobs
//	    }
//	}
//
// Register hooks during application initialization:
//
//	repository.RegisterJobHook(&MyJobTagger{})
type JobHook interface {
	// JobStartCallback is invoked when one or more jobs start.
	// This is called synchronously, so implementations should be fast.
	JobStartCallback(job *schema.Job)

	// JobStopCallback is invoked when a job completes.
	// This is called synchronously, so implementations should be fast.
	JobStopCallback(job *schema.Job)
}

var (
	initOnce sync.Once
	hooks    []JobHook
)

// RegisterJobHook registers a JobHook to receive job lifecycle callbacks.
// Multiple hooks can be registered and will be called in registration order.
// This function is safe to call multiple times and is typically called during
// application initialization.
//
// Nil hooks are silently ignored to simplify conditional registration.
func RegisterJobHook(hook JobHook) {
	initOnce.Do(func() {
		hooks = make([]JobHook, 0)
	})

	if hook != nil {
		hooks = append(hooks, hook)
	}
}

// CallJobStartHooks invokes all registered JobHook.JobStartCallback methods
// for each job in the provided slice. This is called internally by the repository
// when jobs are started (e.g., via StartJob or batch job imports).
//
// Hooks are called synchronously in registration order. If a hook panics,
// the panic will propagate to the caller.
func CallJobStartHooks(jobs []*schema.Job) {
	if hooks == nil {
		return
	}

	for _, hook := range hooks {
		if hook != nil {
			for _, job := range jobs {
				hook.JobStartCallback(job)
			}
		}
	}
}

// CallJobStopHooks invokes all registered JobHook.JobStopCallback methods
// for the provided job. This is called internally by the repository when a
// job completes (e.g., via StopJob or job state updates).
//
// Hooks are called synchronously in registration order. If a hook panics,
// the panic will propagate to the caller.
func CallJobStopHooks(job *schema.Job) {
	if hooks == nil {
		return
	}

	for _, hook := range hooks {
		if hook != nil {
			hook.JobStopCallback(job)
		}
	}
}
