// Package connector is the gateway's gRPC client for the Telegram validator
// side-service: it validates Mini App initData and Login Widget data. The validator
// lives on the trusted internal network and holds the bot token only for HMAC, so
// the connection uses insecure (plaintext) transport credentials (ARCHITECTURE.md
// §12). Out-of-app push no longer goes through this client; it is delivered to the
// remote bot over the reverse mTLS bot-link (gateway/internal/botlink).
package connector

import (
	"context"
	"errors"
	"fmt"

	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/status"

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

// ErrInvalidInitData is returned by ValidateInitData when the connector rejects the
// launch data (a gRPC InvalidArgument), letting the transcode layer surface a stable
// result code.
var ErrInvalidInitData = errors.New("connector: invalid telegram init data")

// ErrInvalidLoginWidget is returned by ValidateLoginWidget when the connector
// rejects the Login Widget data (a gRPC InvalidArgument).
var ErrInvalidLoginWidget = errors.New("connector: invalid telegram login widget data")

// User is a validated Mini App identity. LanguageCode seeds a brand-new account's
// preferred (interface) language; it is empty for a Login Widget validation.
type User struct {
	ExternalID   string
	Username     string
	FirstName    string
	LanguageCode string
}

// Client wraps the connector's Telegram gRPC service.
type Client struct {
	conn *grpc.ClientConn
	c    telegramv1.TelegramClient
}

// New dials the connector gRPC endpoint.
func New(addr string) (*Client, error) {
	conn, err := grpc.NewClient(addr,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
	)
	if err != nil {
		return nil, fmt.Errorf("connector: dial %s: %w", addr, err)
	}
	return &Client{conn: conn, c: telegramv1.NewTelegramClient(conn)}, nil
}

// Close releases the gRPC connection.
func (c *Client) Close() error { return c.conn.Close() }

// ValidateInitData verifies Mini App launch data and returns the user identity,
// mapping a connector InvalidArgument to ErrInvalidInitData.
func (c *Client) ValidateInitData(ctx context.Context, initData string) (User, error) {
	resp, err := c.c.ValidateInitData(ctx, &telegramv1.ValidateInitDataRequest{InitData: initData})
	if err != nil {
		if status.Code(err) == codes.InvalidArgument {
			return User{}, ErrInvalidInitData
		}
		return User{}, err
	}
	return User{
		ExternalID:   resp.GetExternalId(),
		Username:     resp.GetUsername(),
		FirstName:    resp.GetFirstName(),
		LanguageCode: resp.GetLanguageCode(),
	}, nil
}

// ValidateLoginWidget verifies Telegram Login Widget data and returns the user
// identity, mapping a connector InvalidArgument to ErrInvalidLoginWidget. It backs
// the link.telegram edge operation.
func (c *Client) ValidateLoginWidget(ctx context.Context, data string) (User, error) {
	resp, err := c.c.ValidateLoginWidget(ctx, &telegramv1.ValidateLoginWidgetRequest{Data: data})
	if err != nil {
		if status.Code(err) == codes.InvalidArgument {
			return User{}, ErrInvalidLoginWidget
		}
		return User{}, err
	}
	return User{
		ExternalID: resp.GetExternalId(),
		Username:   resp.GetUsername(),
		FirstName:  resp.GetFirstName(),
	}, nil
}
