package http

import (
	"encoding/json"
	"golang.source-fellows.com/training-fellow/booking/internal"
	"net/http"
)

type Problem struct {
	// A URI reference [RFC3986] that identifies the problem type.
	// This specification encourages that, when dereferenced,
	// it provide human-readable documentation for the problem
	// type (e.g., using HTML [W3C.REC-html5-20141028]).
	// When this member is not present, its value is assumed
	// to be "about:blank"
	ProblemType string `json:"problemType"`

	// A short, human-readable summary of the problem
	// type.  It SHOULD NOT change from occurrence to occurrence of the
	// problem, except for purposes of localization (e.g., using
	// proactive content negotiation; see [RFC7231], Section 3.4)
	Title string `json:"title"`

	// The HTTP status code ([RFC7231], Section 6)
	// generated by the origin server for this occurrence of the problem.
	Status int `json:"status"`

	// A human-readable explanation specific to this
	// occurrence of the problem.
	Detail string `json:"detail"`

	// A URI reference that identifies the specific
	// occurrence of the problem.  It may or may not yield further
	// information if dereferenced.
	Instance string `json:"instance,omitempty"`
}

const (
	GeneralProblemType = "https://trainingfellow/errors/general"
)

func NewProblem(details string) Problem {
	return Problem{
		ProblemType: GeneralProblemType,
		Detail:      details,
		Status:      http.StatusInternalServerError,
	}
}

func NewProblemFromErr(err error) (int, Problem) {
	problem := errToProblem(err)
	return problem.Status, problem
}

func NewProblemFromErrCodeWithError(statusCode int, err error) (int, Problem) {
	problem := errToProblem(err)
	problem.Status = statusCode
	return statusCode, problem
}

func NewProblemFromBodyBytes(data []byte) (*Problem, error) {
	var problem Problem
	err := json.Unmarshal(data, &problem)
	if err != nil {
		return nil, err
	}
	return &problem, nil
}

func errToProblem(err error) Problem {
	var problemType string
	var httpErrorCode int
	var detail string
	var title string

	if internal.IsUnavailable(err) {
		problemType = GeneralProblemType
		detail = "could not get data"
		title = "could not get data"
		httpErrorCode = http.StatusInternalServerError
	} else {
		problemType = GeneralProblemType
		detail = err.Error()
		title = "general error"
		httpErrorCode = http.StatusInternalServerError
	}

	return Problem{
		ProblemType: problemType,
		Detail:      detail,
		Status:      httpErrorCode,
		Title:       title,
	}
}
