package rules

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"iwarma.ru/console/correlator/events"
	"net/http"
	"text/template"
	"time"

	log "github.com/sirupsen/logrus"
)

const (
	HttpActionType = "http"
)

// HttpAction Send record via HTTP. Wil use application/json if empty content type set
type HttpAction struct {
	Url          string
	Template     string
	ContentType  string
	bodyTemplate *template.Template
}

func (action HttpAction) GetType() string {
	return HttpActionType
}

func (action *HttpAction) Perform(events *[]*events.Event) error {
	contextLogger := log.WithFields(log.Fields{"type": HttpActionType, "event_count": len(*events)})
	contextLogger.Debug("Start action")
	defer contextLogger.Debug("End action")

	if events == nil {
		contextLogger.Error("Nil events provided")
		return errors.New("nil events provided")
	}

	// Create template
	if action.bodyTemplate == nil {
		bodyTemplate, err := template.New("Action").Parse(action.Template)
		if err != nil {
			return err
		}

		action.bodyTemplate = bodyTemplate
	}

	for _, cur := range *events {
		// Render template
		var buf bytes.Buffer
		err := action.bodyTemplate.Execute(&buf, *cur)
		if err != nil {
			return err
		}

		reader := bytes.NewReader(buf.Bytes())
		http.DefaultClient.Timeout = time.Second * 30
		_, err = http.Post(action.Url, action.ContentType, reader)
		if err != nil {
			return err
		}

	}
	http.DefaultClient.CloseIdleConnections()
	return nil
}

func (action HttpAction) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		CurType     string `json:"type"`
		Url         string `json:"url"`
		Template    string `json:"template"`
		ContentType string `json:"content_type"`
	}{
		CurType:     HttpActionType,
		Url:         action.Url,
		Template:    action.Template,
		ContentType: action.ContentType,
	})
}

func (action *HttpAction) UnmarshalJSON(b []byte) error {

	cur := struct {
		CurType     string `json:"type"`
		Url         string `json:"url"`
		ContentType string `json:"content_type"`
		Template    string `json:"template"`
	}{}

	err := json.Unmarshal(b, &cur)
	if err != nil {
		return err
	}

	if cur.CurType != HttpActionType {
		return fmt.Errorf("bad action type. Expect http, got %s", cur.CurType)
	}

	action.Url = cur.Url
	action.Template = cur.Template
	action.ContentType = cur.ContentType

	return nil
}

func (action *HttpAction) ParseInterface(v interface{}) error {
	m, ok := v.(map[string]interface{})
	if !ok {
		return fmt.Errorf("can't parse %v from %T", v, v)
	}

	action.Url, ok = m["url"].(string)
	if !ok {
		return fmt.Errorf("can't get URL")
	}

	// Template can be in data or template
	if action.Template, ok = m["data"].(string); !ok {
		if action.Template, ok = m["template"].(string); !ok {
			return fmt.Errorf("can't get template")
		}
	}

	if action.ContentType, ok = m["content_type"].(string); !ok {
		return fmt.Errorf("no content type provided")
	}

	return nil
}
