package hub

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"runtime"
	"time"

	"github.com/photoprism/photoprism/internal/form"
	"github.com/photoprism/photoprism/pkg/http/header"
	"github.com/photoprism/photoprism/pkg/txt"
)

// Feedback represents user feedback submitted through the user interface.
type Feedback struct {
	Category      string `json:"Category"`
	Subject       string `json:"Subject"`
	Message       string `json:"Message"`
	UserName      string `json:"UserName"`
	UserEmail     string `json:"UserEmail"`
	UserAgent     string `json:"UserAgent"`
	ApiKey        string `json:"ApiKey"`
	ClientVersion string `json:"ClientVersion"`
	ClientSerial  string `json:"ClientSerial"`
	ClientOS      string `json:"ClientOS"`
	ClientArch    string `json:"ClientArch"`
	ClientCPU     int    `json:"ClientCPU"`
	ClientEnv     string `json:"ClientEnv"`
	PartnerID     string `json:"PartnerID"`
}

// NewFeedback creates a new hub feedback instance.
func NewFeedback(version, serial, env, partnerId string) *Feedback {
	return &Feedback{
		ClientVersion: version,
		ClientSerial:  serial,
		ClientOS:      runtime.GOOS,
		ClientArch:    runtime.GOARCH,
		ClientCPU:     runtime.NumCPU(),
		ClientEnv:     env,
		PartnerID:     partnerId,
	}
}

// SendFeedback sends a feedback message.
func (c *Config) SendFeedback(frm form.Feedback) (err error) {
	feedback := NewFeedback(c.Version, c.Serial, c.Env, c.PartnerID)
	feedback.Category = frm.Category
	feedback.Subject = txt.Shorten(frm.Message, 50, "...")
	feedback.Message = frm.Message
	feedback.UserName = frm.UserName
	feedback.UserEmail = frm.UserEmail
	feedback.UserAgent = frm.UserAgent
	feedback.ApiKey = c.Key

	// Create new http.Client instance.
	//
	// NOTE: Timeout specifies a time limit for requests made by
	// this Client. The timeout includes connection time, any
	// redirects, and reading the response body. The timer remains
	// running after Get, Head, Post, or Do return and will
	// interrupt reading of the Response.Body.
	client := &http.Client{Timeout: 60 * time.Second}

	// Get feedback endpoint URL.
	endpointUrl := GetFeedbackServiceURL(c.Key)

	// Return if no endpoint URL is set.
	if endpointUrl == "" {
		return errors.New("unable to send feedback (service disabled)")
	}

	method := http.MethodPost

	var req *http.Request

	log.Debugf("config: sending feedback to %s", GetServiceHost())

	if j, reqErr := json.Marshal(feedback); reqErr != nil {
		return reqErr
	} else if req, reqErr = http.NewRequest(method, endpointUrl, bytes.NewReader(j)); reqErr != nil {
		return reqErr
	}

	// Set user agent.
	if c.UserAgent != "" {
		req.Header.Set(header.UserAgent, c.UserAgent)
	} else {
		req.Header.Set(header.UserAgent, "PhotoPrism/Test")
	}

	req.Header.Add(header.AcceptLanguage, frm.UserLocales)
	req.Header.Add(header.ContentType, header.ContentTypeJson)

	var r *http.Response

	for i := 0; i < 3; i++ {
		r, err = client.Do(req)

		if err == nil {
			break
		}
	}

	if err != nil {
		return err
	} else if r.StatusCode >= 400 {
		err = fmt.Errorf("request to %s failed (error %d)", GetServiceHost(), r.StatusCode)
		return err
	}

	err = json.NewDecoder(r.Body).Decode(c)

	if err != nil {
		return err
	}

	return nil
}
