package process

import (
	"context"
	"time"

	"github.com/grafana/grafana/pkg/plugins"
)

const defaultKeepPluginAliveTickerDuration = time.Second

type Service struct {
	keepPluginAliveTickerDuration time.Duration
}

func ProvideService() *Service {
	return &Service{
		keepPluginAliveTickerDuration: defaultKeepPluginAliveTickerDuration,
	}
}

func (s *Service) Start(ctx context.Context, p *plugins.Plugin) error {
	if !p.IsManaged() || !p.Backend || p.Error != nil {
		return nil
	}

	if err := s.startPluginAndKeepItAlive(ctx, p); err != nil {
		return err
	}

	p.Logger().Debug("Successfully started backend plugin process")
	return nil
}

func (*Service) Stop(ctx context.Context, p *plugins.Plugin) error {
	p.Logger().Debug("Stopping plugin process")
	if err := p.Decommission(); err != nil {
		return err
	}

	if err := p.Stop(ctx); err != nil {
		return err
	}

	return nil
}

func (s *Service) startPluginAndKeepItAlive(ctx context.Context, p *plugins.Plugin) error {
	if err := p.Start(ctx); err != nil {
		return err
	}

	if p.IsCorePlugin() {
		return nil
	}

	go func(p *plugins.Plugin) {
		if err := s.keepPluginAlive(p); err != nil {
			p.Logger().Error("Attempt to restart killed plugin process failed", "error", err)
		}
	}(p)

	return nil
}

// keepPluginAlive will restart the plugin if the process is killed or exits
func (s *Service) keepPluginAlive(p *plugins.Plugin) error {
	ticker := time.NewTicker(s.keepPluginAliveTickerDuration)

	for {
		<-ticker.C
		if p.IsDecommissioned() {
			p.Logger().Debug("Plugin decommissioned")
			return nil
		}

		if !p.Exited() {
			continue
		}

		p.Logger().Debug("Restarting plugin")
		if err := p.Start(context.Background()); err != nil {
			p.Logger().Error("Failed to restart plugin", "error", err)
			continue
		}
		p.Logger().Debug("Plugin restarted")
	}
}
