package webhooks

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"regexp"

	"github.com/pkg/errors"
	"github.com/runatlantis/atlantis/server/logging"
)

// HttpWebhook sends webhooks to any HTTP destination.
type HttpWebhook struct {
	Client         *HttpClient
	WorkspaceRegex *regexp.Regexp
	BranchRegex    *regexp.Regexp
	URL            string
}

// Send sends the webhook to URL if workspace and branch matches their respective regex.
func (h *HttpWebhook) Send(_ logging.SimpleLogging, applyResult ApplyResult) error {
	if !h.WorkspaceRegex.MatchString(applyResult.Workspace) || !h.BranchRegex.MatchString(applyResult.Pull.BaseBranch) {
		return nil
	}
	if err := h.doSend(applyResult); err != nil {
		return errors.Wrap(err, fmt.Sprintf("sending webhook to %q", h.URL))
	}
	return nil
}

func (h *HttpWebhook) doSend(applyResult ApplyResult) error {
	body, err := json.Marshal(applyResult)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", h.URL, bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	for header, values := range h.Client.Headers {
		for _, value := range values {
			req.Header.Add(header, value)
		}
	}
	resp, err := h.Client.Client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		respBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("returned status code %d with response %q", resp.StatusCode, respBody)
	}
	return nil
}

// HttpClient wraps http.Client allowing to add arbitrary Headers to a request.
type HttpClient struct {
	Client  *http.Client
	Headers map[string][]string
}
