package rest

import (
	"fmt"
	"net/http"
	"net/url"
	"runtime"
	"strings"

	"github.com/go-chi/render"
	log "github.com/go-pkgz/lgr"
	"github.com/go-pkgz/rest"
)

// All error codes for UI mapping and translation
const (
	ErrInternal           = 0  // any internal error
	ErrCommentNotFound    = 1  // can't find comment
	ErrDecode             = 2  // failed to unmarshal incoming request
	ErrNoAccess           = 3  // rejected by auth
	ErrCommentValidation  = 4  // validation failed
	ErrPostNotFound       = 5  // can't find post
	ErrSiteNotFound       = 6  // can't find site
	ErrUserBlocked        = 7  // user blocked
	ErrReadOnly           = 8  // write failed on read only
	ErrCommentRejected    = 9  // general error on rejected comment change
	ErrCommentEditExpired = 10 // too late for edit
	ErrCommentEditChanged = 11 // parent comment cannot be changed
	ErrVoteRejected       = 12 // general error on vote rejected
	ErrVoteSelf           = 13 // vote for own comment
	ErrVoteDbl            = 14 // already voted for the comment
	ErrVoteMax            = 15 // too many votes for the comment
	ErrVoteMinScore       = 16 // min score reached for the comment
	ErrActionRejected     = 17 // general error for rejected actions
	ErrAssetNotFound      = 18 // requested file not found
)

// SendErrorJSON makes {error: blah, details: blah} json body and responds with error code
func SendErrorJSON(w http.ResponseWriter, r *http.Request, httpStatusCode int, err error, details string, errCode int) {
	log.Printf("[DEBUG] %s", errDetailsMsg(r, httpStatusCode, err, details, errCode))
	render.Status(r, httpStatusCode)
	render.JSON(w, r, rest.JSON{"error": err.Error(), "details": details, "code": errCode})
}

func errDetailsMsg(r *http.Request, httpStatusCode int, err error, details string, errCode int) string {
	uinfoStr := ""
	if user, e := GetUserInfo(r); e == nil {
		uinfoStr = user.Name + "/" + user.ID + " - "
	}
	q := r.URL.String()
	if qun, e := url.QueryUnescape(q); e == nil {
		q = qun
	}

	srcFileInfo := ""
	if pc, file, line, ok := runtime.Caller(2); ok {
		fnameElems := strings.Split(file, "/")
		funcNameElems := strings.Split(runtime.FuncForPC(pc).Name(), "/")
		srcFileInfo = fmt.Sprintf(" [caused by %s:%d %s]", strings.Join(fnameElems[len(fnameElems)-3:], "/"),
			line, funcNameElems[len(funcNameElems)-1])
	}

	remoteIP := r.RemoteAddr
	if pos := strings.Index(remoteIP, ":"); pos >= 0 {
		remoteIP = remoteIP[:pos]
	}
	return fmt.Sprintf("%s - %v - %d (%d) - %s%s - %s%s",
		details, err, httpStatusCode, errCode, uinfoStr, remoteIP, q, srcFileInfo)
}
