package main

import (
	"context"
	"errors"
	"net/http"
	"time"

	"git.xeserv.us/xena/route/internal/database"
	"github.com/Xe/ln"
	"github.com/twitchtv/twirp"
	"golang.org/x/net/trace"
)

// errors
var (
	ErrNotAuthorized = errors.New("server: not authorized")
)

func (s *Server) makeTwirpHooks() *twirp.ServerHooks {
	hooks := &twirp.ServerHooks{}

	hooks.RequestRouted = func(ctx context.Context) (context.Context, error) {
		ctx = withStartTime(ctx)

		method, ok := twirp.MethodName(ctx)
		if !ok {
			return ctx, nil
		}

		pkg, ok := twirp.PackageName(ctx)
		if !ok {
			return ctx, nil
		}

		svc, ok := twirp.ServiceName(ctx)
		if !ok {
			return ctx, nil
		}

		ctx = ln.WithF(ctx, ln.F{
			"twirp_method":  method,
			"twirp_package": pkg,
			"twirp_service": svc,
		})

		hdr, ok := twirp.HTTPRequestHeaders(ctx)
		if !ok {
			return ctx, errors.New("can't get request headers")
		}

		req, _ := http.NewRequest("GET", "/", nil)
		req.Header = hdr

		ck, err := req.Cookie("routed")
		if err != nil {
			return ctx, err
		}

		tok := ck.Value

		t, err := s.db.GetToken(ctx, tok)
		if err != nil {
			return ctx, err
		}

		ctx = withAuthToken(ctx, t)
		ctx = ln.WithF(ctx, t.F())

		return ctx, nil
	}

	hooks.ResponseSent = func(ctx context.Context) {
		f := ln.F{}
		now := time.Now()
		t, ok := getStartTime(ctx)
		if ok {
			f["response_time"] = now.Sub(t)
		}

		ln.Log(ctx, f, ln.Action("response sent"))
	}

	hooks.Error = func(ctx context.Context, e twirp.Error) context.Context {
		f := ln.F{}

		for k, v := range e.MetaMap() {
			f["twirp_meta_"+k] = v
		}

		ln.Error(ctx, e, f, ln.Action("twirp error"), ln.F{
			"twirp_error_code": e.Code(),
			"twirp_error_msg":  e.Msg(),
		})

		return ctx
	}

	return hooks
}

func (s *Server) getAuth(ctx context.Context, operation, scope string) (database.Token, error) {
	t, ok := getAuthToken(ctx)
	if !ok {
		return database.Token{}, errors.New("no auth token in context")
	}

	ok = false
	for _, sc := range t.Scopes {
		if sc == scope {
			ok = true
		}
	}

	if !ok {
		return database.Token{}, ErrNotAuthorized
	}

	ln.WithF(ctx, ln.F{"operation": operation})

	return t, nil
}

func handleError(ctx context.Context, clitok database.Token, err error, f ln.F) error {
	tr, ok := trace.FromContext(ctx)
	if !ok {
		goto skip
	}
	tr.SetError()

skip:
	ln.Error(ctx, err, f, clitok)

	return err
}
