package notifier

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

// Slack holds the hook URL
type Slack struct {
	URL       string
	Username  string
	Channel   string
	IconEmoji string
}

// SlackPayload holds the channel and attachments
type SlackPayload struct {
	Channel     string            `json:"channel"`
	Username    string            `json:"username"`
	IconUrl     string            `json:"icon_url"`
	IconEmoji   string            `json:"icon_emoji"`
	Text        string            `json:"text,omitempty"`
	Attachments []SlackAttachment `json:"attachments,omitempty"`
}

// SlackAttachment holds the markdown message body
type SlackAttachment struct {
	Color      string       `json:"color"`
	AuthorName string       `json:"author_name"`
	Text       string       `json:"text"`
	MrkdwnIn   []string     `json:"mrkdwn_in"`
	Fields     []SlackField `json:"fields"`
}

type SlackField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// NewSlack validates the Slack URL and returns a Slack object
func NewSlack(hookURL string, username string, channel string) (*Slack, error) {
	_, err := url.ParseRequestURI(hookURL)
	if err != nil {
		return nil, fmt.Errorf("invalid Slack hook URL %s", hookURL)
	}

	if username == "" {
		return nil, errors.New("empty Slack username")
	}

	if channel == "" {
		return nil, errors.New("empty Slack channel")
	}

	return &Slack{
		Channel:   channel,
		URL:       hookURL,
		Username:  username,
		IconEmoji: ":rocket:",
	}, nil
}

// Post Slack message
func (s *Slack) Post(workload string, namespace string, message string, fields []SlackField, warn bool) error {
	payload := SlackPayload{
		Channel:  s.Channel,
		Username: s.Username,
	}

	color := "good"
	if warn {
		color = "danger"
	}

	a := SlackAttachment{
		Color:      color,
		AuthorName: fmt.Sprintf("%s.%s", workload, namespace),
		Text:       message,
		MrkdwnIn:   []string{"text"},
		Fields:     fields,
	}

	payload.Attachments = []SlackAttachment{a}

	data, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("marshalling slack payload failed %v", err)
	}

	b := bytes.NewBuffer(data)

	if res, err := http.Post(s.URL, "application/json", b); err != nil {
		return fmt.Errorf("sending data to slack failed %v", err)
	} else {
		defer res.Body.Close()
		statusCode := res.StatusCode
		if statusCode != 200 {
			body, _ := ioutil.ReadAll(res.Body)
			return fmt.Errorf("sending data to slack failed %v", string(body))
		}
	}

	return nil
}
