package api

import (
	"fmt"
	"net/http"

	"google.golang.org/api/youtube/v3"

	"github.com/cactus/go-statsd-client/statsd"
	"github.com/jixwanwang/apiutils"

	"goji.io"
	"goji.io/pat"
	"golang.org/x/net/context"

	"code.justin.tv/common/config"
	"code.justin.tv/common/golibs/errorlogger"
	"code.justin.tv/common/twitchhttp"

	yt "code.justin.tv/live/yt-live-annotations/youtube"
)

// Server is a server type
type Server struct {
	*goji.Mux
	backend     Backend
	yt          YT
	stats       statsd.Statter
	errorLogger errorlogger.ErrorLogger
}

type Backend interface {
	EnableAnnotations(ctx context.Context, userID string) error
	DisableAnnotations(ctx context.Context, userID string) error
	GetAnnotationsEnabled(ctx context.Context, userID string) (bool, error)
}

type YT interface {
	GetYoutubeAnnotations(ctx context.Context, userID string) (*youtube.InvideoPromotion, error)
	ClearYoutubeAnnotations(ctx context.Context, userID string) error
	SetYoutubeAnnotationsStatus(ctx context.Context, userID string, status int) error
}

// NewServer creates a new server
func NewServer(b Backend, yt YT) (*Server, error) {
	s := &Server{
		Mux:         twitchhttp.NewServer(),
		backend:     b,
		yt:          yt,
		stats:       config.Statsd(),
		errorLogger: config.RollbarErrorLogger(),
	}

	s.HandleFuncC(pat.Get("/health"), s.HealthCheck)

	s.HandleFuncC(pat.Get("/enabled/:user_id"), s.getAnnotationsEnabled)
	s.HandleFuncC(pat.Post("/enabled/:user_id"), s.setAnnotationsEnabled)
	s.HandleFuncC(pat.Delete("/enabled/:user_id"), s.setAnnotationsDisabled)

	s.HandleFuncC(pat.Get("/annotations/:user_id"), s.getExistingAnnotations)
	s.HandleFuncC(pat.Post("/annotations/:user_id"), s.addAnnotation)
	s.HandleFuncC(pat.Delete("/annotations/:user_id"), s.removeAnnotation)
	s.HandleFuncC(pat.Delete("/annotations/:user_id/all"), s.clearAllAnnotations)
	return s, nil
}

// HealthCheck implementes a health check endpoint
func (s *Server) HealthCheck(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	_, err := s.backend.GetAnnotationsEnabled(ctx, "0")
	if err != nil {
		s.serveError(w, r, http.StatusInternalServerError, fmt.Errorf("dyanmo error"), "health")
		return
	}
	s.serveSuccess(w, "OK", "health")
}

func (S *Server) serveSuccess(w http.ResponseWriter, response interface{}, statName string) {
	S.stats.Inc(fmt.Sprintf("api.%v.200", statName), 1, 0.5)

	apiutils.ServeJSON(w, response)
}

func shouldReport(errRes apiutils.ErrorResponse) bool {
	return errRes.Status >= 500 && !yt.ShouldIgnoreError(errRes)
}

func (S *Server) serveError(w http.ResponseWriter, r *http.Request, status int, err error, statName string) {
	S.stats.Inc(fmt.Sprintf("api.%v.%v", statName, status), 1, 0.5)
	errRes := apiutils.NewErrorResponse(status, err.Error())
	apiutils.ServeError(w, errRes)

	if S.errorLogger != nil && shouldReport(errRes) {
		S.errorLogger.RequestError(r, errRes)
	}
}
