package botlink

import (
	"context"
	"errors"
	"time"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	botlinkv1 "scrabble/pkg/proto/botlink/v1"
	telegramv1 "scrabble/pkg/proto/telegram/v1"
)

// RelayServer lets the backend's admin console reach the remote bot through the
// gateway: it implements the two admin delivery methods of the Telegram service by
// forwarding them onto the bot-link and awaiting the bot's Ack. The validation and
// out-of-app push methods are intentionally unimplemented here — login validation
// runs against the home validator and out-of-app push goes straight to the Hub.
type RelayServer struct {
	telegramv1.UnimplementedTelegramServer
	hub      *Hub
	deadline time.Duration
}

// NewRelayServer builds the relay over hub. deadline bounds the wait for the bot's
// Ack before reporting the send as not delivered.
func NewRelayServer(hub *Hub, deadline time.Duration) *RelayServer {
	return &RelayServer{hub: hub, deadline: deadline}
}

// SendToUser forwards an admin message for one user to the bot and reports whether
// it was delivered. A missing bot maps to Unavailable (parity with an unreachable
// connector); a deadline before the Ack reports delivered=false.
func (r *RelayServer) SendToUser(ctx context.Context, req *telegramv1.SendToUserRequest) (*telegramv1.SendResponse, error) {
	delivered, err := r.await(ctx, SendToUserCommand(req.GetExternalId(), req.GetText()))
	if err != nil {
		return nil, err
	}
	return &telegramv1.SendResponse{Delivered: delivered}, nil
}

// SendToGameChannel forwards an admin message for the game channel to the bot and
// reports whether it was delivered.
func (r *RelayServer) SendToGameChannel(ctx context.Context, req *telegramv1.SendToGameChannelRequest) (*telegramv1.SendResponse, error) {
	delivered, err := r.await(ctx, SendToGameChannelCommand(req.GetText()))
	if err != nil {
		return nil, err
	}
	return &telegramv1.SendResponse{Delivered: delivered}, nil
}

// await sends cmd with the relay deadline and translates the Hub outcome into the
// gRPC contract the backend client expects.
func (r *RelayServer) await(ctx context.Context, cmd *botlinkv1.Command) (bool, error) {
	cctx, cancel := context.WithTimeout(ctx, r.deadline)
	defer cancel()
	delivered, err := r.hub.SendAwait(cctx, cmd)
	switch {
	case errors.Is(err, ErrNoBot):
		return false, status.Error(codes.Unavailable, "no telegram bot connected")
	case err != nil:
		return false, status.Error(codes.Internal, err.Error())
	default:
		return delivered, nil
	}
}
