package notifier

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

const (
	// IOS identifier for an iOS notification.
	IOS = 1
	// Android identifier for an android notification.
	Android             = 2
	failedPushErrorType = "failed-push"
	pushEndpoint        = "/api/push"
)

// Notifier handles android and ios push notifications.
type Notifier struct {
	client *http.Client
	url    string
}

// New notifier connected to the specified server.
func New(url string) *Notifier {
	client := &http.Client{}

	return &Notifier{url: url, client: client}
}

// Notification details for gorush.
type Notification struct {
	Tokens   []string `json:"tokens"`
	Platform float32  `json:"platform"`
	Message  string   `json:"message"`
}

type request struct {
	Notifications []*Notification `json:"notifications"`
}

// Response from gorush.
type Response struct {
	Logs []struct {
		Type  string `json:"type"`
		Error string `json:"error"`
	} `json:"logs"`
}

// Send a push notification to given devices.
func (n *Notifier) Send(notifications []*Notification) error {
	url := n.url + pushEndpoint
	r := request{Notifications: notifications}

	body, err := json.Marshal(r)
	if err != nil {
		return err
	}

	res, err := n.doRequest(url, body)
	if err != nil {
		return err
	}

	if len(res.Logs) > 0 {
		if res.Logs[0].Type == failedPushErrorType {
			return errors.New(res.Logs[0].Error)
		}
	}

	return err
}

func (n *Notifier) doRequest(url string, body []byte) (res Response, err error) {
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
	if err != nil {
		return
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := n.client.Do(req)
	if err != nil {
		return
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Println(err.Error())
		}
	}()

	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	return res, json.Unmarshal(body, &res)
}