package main

import (
	"errors"

	"git.xeserv.us/xena/route/internal/database"
	proto "git.xeserv.us/xena/route/proto"
	"github.com/Xe/ln"
	"github.com/Xe/uuid"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// Token is the token server implementation for grpc use.
type Token struct {
	*Server
}

// interface assertions
var (
	_ proto.Tokens = &Token{}
)

func (t *Token) Get(ctx context.Context, req *proto.GetTokenRequest) (*proto.Token, error) {
	clitok, err := t.getAuth(ctx, "get single token", "token:get")
	if err != nil {
		return nil, handleError(ctx, clitok, err, ln.F{"action": "not authorized"})
	}

	if req.Id == "" {
		return nil, status.Errorf(codes.InvalidArgument, "must specify ID")
	}

	dbt, err := t.db.Tokens().Get(ctx, req.Id)
	if err != nil {
		return nil, err
	}

	if dbt.Owner != clitok.Owner {
		return nil, ErrNotAuthorized
	}

	return dbt.AsProto(), nil
}

func (t *Token) GetAll(ctx context.Context, req *proto.Nil) (*proto.TokenSet, error) {
	clitok, err := t.getAuth(ctx, "get all tokens", "token:getall")
	if err != nil {
		return nil, handleError(ctx, clitok, err, ln.F{"action": "Token.GetAll_getAuth"})
	}

	toks, err := t.db.Tokens().GetAll(ctx, clitok.Owner)
	if err != nil {
		return nil, handleError(ctx, clitok, err, ln.F{"action": "Token.GetAll_db.GetTokensForOwner"})
	}

	result := []*proto.Token{}

	for _, tok := range toks {
		result = append(result, tok.AsProto())
	}

	return &proto.TokenSet{
		Tokens: result,
	}, nil
}

func (t *Token) Put(ctx context.Context, tok *proto.Token) (*proto.Token, error) {
	clitok, err := t.getAuth(ctx, "put new token", "token:put")
	if err != nil {
		return nil, handleError(ctx, clitok, err, ln.F{"action": "not authorized"})
	}

	dtok := database.Token{
		ID:     uuid.New(),
		Owner:  clitok.Owner,
		Scopes: tok.Scopes,
	}

	dbt, err := t.db.Tokens().Put(ctx, dtok)
	if err != nil {
		return nil, handleError(ctx, clitok, err, ln.F{"action": "put token into database", "scopes": tok.Scopes})
	}

	ln.Log(ctx, dbt, ln.Action("new token created"))

	return dbt.AsProto(), nil
}

func (t *Token) Delete(ctx context.Context, tok *proto.Token) (*proto.Nil, error) {
	clitok, err := t.getAuth(ctx, "delete single token", "token:delete")
	if err != nil {
		return nil, handleError(ctx, clitok, err, ln.F{"action": "not authorized"})
	}

	_, err = t.db.Tokens().Delete(ctx, tok.Id)
	if err != nil {
		return nil, handleError(ctx, clitok, err, ln.F{"action": "delete token from database", "token_id": tok.Id})
	}

	ln.Log(ctx, clitok, ln.Action("token deleted"), ln.F{"token_id": tok.Id})

	return &proto.Nil{}, nil
}

func (t *Token) Deactivate(ctx context.Context, tok *proto.Token) (*proto.Nil, error) {
	return nil, errors.New("not implemented")
}
