package notification

import (
	"context"
	"sync"
	"time"

	"code.justin.tv/eventbus/controlplane/infrastructure/notification/state"
	"code.justin.tv/eventbus/controlplane/infrastructure/validation"
	"github.com/PagerDuty/go-pagerduty"
	"github.com/pkg/errors"
)

const (
	actionResolve = "resolve"
	actionTrigger = "trigger"
)

var severity = map[validation.Status]string{
	validation.StatusWarn:  "warning",
	validation.StatusError: "error",
}

type pagerdutyActions struct {
	high string
	low  string
}

func (p *pagerdutyActions) Open(report *validation.Report) error {
	return p.trigger(report)
}

func (p *pagerdutyActions) ChangeStatus(oldReport *validation.Report, newReport *validation.Report) error {
	err := p.resolve(oldReport)
	if err != nil {
		return errors.Wrap(err, "unable to resolve pagerduty incident during status change")
	}

	return errors.Wrap(p.trigger(newReport), "could not open pagerduty incident during status change")
}

func (p *pagerdutyActions) Renotify(report *validation.Report) error {
	return p.trigger(report)
}

func (p *pagerdutyActions) Resolve(problemReport *validation.Report, resolutionReport *validation.Report) error {
	return p.resolve(problemReport)
}

func (p *pagerdutyActions) trigger(report *validation.Report) error {
	_, err := pagerduty.ManageEvent(pagerduty.V2Event{
		RoutingKey: p.routingKey(report.Status),
		Action:     actionTrigger,
		DedupKey:   report.Item.ID(),
		Payload: &pagerduty.V2Payload{
			Summary:  report.Message,
			Source:   "eventbus-validator",
			Severity: severity[report.Status],
			Details:  report.Item.Attributes(),
		},
	})
	return err
}

func (p *pagerdutyActions) resolve(report *validation.Report) error {
	_, err := pagerduty.ManageEvent(pagerduty.V2Event{
		RoutingKey: p.routingKey(report.Status),
		Action:     actionResolve,
		DedupKey:   report.Item.ID(),
	})
	return err
}

func (p *pagerdutyActions) routingKey(status validation.Status) string {
	switch status {
	case validation.StatusWarn:
		return p.low
	case validation.StatusError, validation.StatusValidationError:
		return p.high
	default:
		return "" // unknown behavior
	}
}

type PagerDuty struct {
	actions      *pagerdutyActions
	stateTracker *state.Tracker
	reports      []*validation.Report
	mutex        sync.Mutex
}

func NewPagerDuty(low, high string) *PagerDuty {
	actions := &pagerdutyActions{
		low:  low,
		high: high,
	}
	return &PagerDuty{
		actions:      actions,
		stateTracker: state.NewTracker(actions, 20*time.Minute, 2),
	}
}

func (p *PagerDuty) Add(report *validation.Report) {
	p.mutex.Lock()
	p.reports = append(p.reports, report)
	p.mutex.Unlock()
}

func (p *PagerDuty) Submit(ctx context.Context) error {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	for _, report := range p.reports {
		err := p.stateTracker.Handle(report)
		if err != nil {
			return errors.Wrap(err, "could not handle report for pagerduty")
		}
	}

	p.reset()
	return nil
}

func (p *PagerDuty) reset() {
	p.reports = nil
}
