package wallet

import (
	"context"
	"fmt"
	"os"
	"strings"
	"time"
)

const (
	WalletConnectStatusStub     = "stub"
	WalletConnectStatusClient   = "client"
	WalletConnectStatusDisabled = "disabled"
)

// Config describes the public WalletConnect v2 posture exposed to clients.
type Config struct {
	Status          string   `json:"status"`
	Enabled         bool     `json:"enabled"`
	ProjectID       string   `json:"projectId"`
	RelayURL        string   `json:"relayUrl"`
	MetadataURL     string   `json:"metadataUrl"`
	RequiredNamespaces []string `json:"requiredNamespaces"`
	SupportedChains []int    `json:"supportedChains"`
	FallbackAuth    string   `json:"fallbackAuth"`
	Message         string   `json:"message"`
	UpdatedAt       string   `json:"updatedAt"`
}

// ConnectResponse is returned while WalletConnect session bridging remains a stub.
type ConnectResponse struct {
	Status      string `json:"status"`
	Enabled     bool   `json:"enabled"`
	URI         string `json:"uri,omitempty"`
	SessionID   string `json:"sessionId,omitempty"`
	ExpiresAt   string `json:"expiresAt,omitempty"`
	FallbackAuth string `json:"fallbackAuth"`
	Message     string `json:"message"`
}

// Session represents a wallet session snapshot for future WalletConnect integration.
type Session struct {
	SessionID string `json:"sessionId"`
	Address   string `json:"address,omitempty"`
	ChainID   int    `json:"chainId,omitempty"`
	Connected bool   `json:"connected"`
	Status    string `json:"status"`
	Message   string `json:"message"`
}

// WalletConnect handles WalletConnect v2 integration posture for the explorer API.
type WalletConnect struct {
	projectID string
	relayURL  string
	chainID   int
}

// NewWalletConnect creates a WalletConnect handler using deployment env vars.
func NewWalletConnect(chainID int) *WalletConnect {
	projectID := strings.TrimSpace(os.Getenv("WALLETCONNECT_PROJECT_ID"))
	relayURL := strings.TrimSpace(os.Getenv("WALLETCONNECT_RELAY_URL"))
	if relayURL == "" {
		relayURL = "wss://relay.walletconnect.org"
	}
	return &WalletConnect{
		projectID: projectID,
		relayURL:  relayURL,
		chainID:   chainID,
	}
}

func (wc *WalletConnect) enabled() bool {
	return wc.projectID != ""
}

// PublicConfig returns the read-only WalletConnect config surface for clients.
func (wc *WalletConnect) PublicConfig() Config {
	status := WalletConnectStatusDisabled
	if wc.enabled() {
		status = WalletConnectStatusClient
	}
	return Config{
		Status:             status,
		Enabled:            wc.enabled(),
		ProjectID:          wc.projectID,
		RelayURL:           wc.relayURL,
		MetadataURL:        "/api/v1/walletconnect/metadata",
		RequiredNamespaces: []string{"eip155"},
		SupportedChains:    []int{wc.chainID, 1},
		FallbackAuth:       "/api/v1/auth/wallet",
		Message:            wc.publicMessage(),
		UpdatedAt:          time.Now().UTC().Format(time.RFC3339),
	}
}

func (wc *WalletConnect) publicMessage() string {
	if wc.enabled() {
		return "WalletConnect v2 is enabled. Use the WalletConnect button on /wallet for mobile QR pairing; browser extension wallets can continue using /api/v1/auth/wallet."
	}
	return "WalletConnect v2 is not configured. Set WALLETCONNECT_PROJECT_ID to publish relay config; browser wallet auth remains available at /api/v1/auth/wallet."
}

// Connect reports client-side WalletConnect posture. Pairing runs in the browser when projectId is published.
func (wc *WalletConnect) Connect(_ context.Context) (*ConnectResponse, error) {
	if !wc.enabled() {
		return &ConnectResponse{
			Status:       WalletConnectStatusDisabled,
			Enabled:      false,
			FallbackAuth: "/api/v1/auth/wallet",
			Message:      wc.publicMessage(),
		}, fmt.Errorf("walletconnect is disabled")
	}
	return &ConnectResponse{
		Status:       "client",
		Enabled:      true,
		FallbackAuth: "/api/v1/auth/wallet",
		Message:      "Initialize WalletConnect in the browser via /wallet using the published projectId; authenticate with /api/v1/auth/wallet after pairing.",
	}, nil
}

// GetSession returns a registered browser-paired WalletConnect session snapshot.
func (wc *WalletConnect) GetSession(_ context.Context, sessionID string) (*Session, error) {
	if strings.TrimSpace(sessionID) == "" {
		return nil, fmt.Errorf("session id is required")
	}
	if session, ok := lookupWalletConnectSession(sessionID); ok {
		return session, nil
	}
	return &Session{
		SessionID: sessionID,
		Connected: false,
		Status:    WalletConnectStatusClient,
		Message:   "Session not registered yet. Pair on /wallet, then POST /api/v1/walletconnect/session with sessionId and address.",
	}, fmt.Errorf("walletconnect session not found")
}

// RegisterSession stores a client-paired WalletConnect session for operator lookup.
func (wc *WalletConnect) RegisterSession(_ context.Context, sessionID, address string, chainID int) (*Session, error) {
	if strings.TrimSpace(sessionID) == "" {
		return nil, fmt.Errorf("session id is required")
	}
	if !strings.HasPrefix(strings.ToLower(address), "0x") || len(address) != 42 {
		return nil, fmt.Errorf("valid address is required")
	}
	if chainID <= 0 {
		chainID = wc.chainID
	}
	return RegisterClientSession(sessionID, address, chainID), nil
}
