package slack

import (
	"bytes"
	"encoding/json"
	"net/http"
	"time"

	"github.com/porter-dev/porter/api/types"
	"github.com/porter-dev/porter/internal/models/integrations"
	"github.com/porter-dev/porter/internal/notifier"
)

type DeploymentNotifier struct {
	slackInts []*integrations.SlackIntegration
	Config    *types.NotificationConfig
}

func NewDeploymentNotifier(conf *types.NotificationConfig, slackInts ...*integrations.SlackIntegration) *DeploymentNotifier {
	return &DeploymentNotifier{
		slackInts: slackInts,
		Config:    conf,
	}
}

type SlackPayload struct {
	Blocks []*SlackBlock `json:"blocks"`
}

type SlackBlock struct {
	Type string     `json:"type"`
	Text *SlackText `json:"text,omitempty"`
}

type SlackText struct {
	Type string `json:"type"`
	Text string `json:"text"`
}

func (s *DeploymentNotifier) Notify(opts *notifier.NotifyOpts) error {
	if s.Config != nil {
		if !s.Config.Enabled {
			return nil
		}
		if opts.Status == notifier.StatusHelmDeployed && !s.Config.Success {
			return nil
		}
		if opts.Status == notifier.StatusPodCrashed && !s.Config.Failure {
			return nil
		}
		if opts.Status == notifier.StatusHelmFailed && !s.Config.Failure {
			return nil
		}
	}

	// we create a basic payload as a fallback if the detailed payload with "info" fails, due to
	// marshaling errors on the Slack API side.
	blocks, basicBlocks := getSlackBlocks(opts)

	slackPayload := &SlackPayload{
		Blocks: blocks,
	}

	basicSlackPayload := &SlackPayload{
		Blocks: basicBlocks,
	}

	basicPayload, err := json.Marshal(basicSlackPayload)
	if err != nil {
		return err
	}

	payload, err := json.Marshal(slackPayload)
	if err != nil {
		return err
	}

	basicReqBody := bytes.NewReader(basicPayload)
	reqBody := bytes.NewReader(payload)
	client := &http.Client{
		Timeout: time.Second * 5,
	}

	for _, slackInt := range s.slackInts {
		resp, err := client.Post(string(slackInt.Webhook), "application/json", reqBody)

		if err != nil || resp.StatusCode != 200 {
			client.Post(string(slackInt.Webhook), "application/json", basicReqBody)
		}
	}

	return nil
}
