830 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			830 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'package:easy_localization/easy_localization.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:gap/gap.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
import 'package:island/models/wallet.dart';
 | 
						|
import 'package:island/pods/network.dart';
 | 
						|
import 'package:island/screens/wallet.dart';
 | 
						|
import 'package:island/services/time.dart';
 | 
						|
import 'package:island/widgets/alert.dart';
 | 
						|
import 'package:island/widgets/content/sheet.dart';
 | 
						|
import 'package:island/widgets/payment/payment_overlay.dart';
 | 
						|
import 'package:material_symbols_icons/symbols.dart';
 | 
						|
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
						|
import 'package:styled_widget/styled_widget.dart';
 | 
						|
 | 
						|
part 'lottery.g.dart';
 | 
						|
 | 
						|
@riverpod
 | 
						|
Future<List<SnLotteryTicket>> lotteryTickets(
 | 
						|
  Ref ref, {
 | 
						|
  int offset = 0,
 | 
						|
  int take = 20,
 | 
						|
}) async {
 | 
						|
  final client = ref.watch(apiClientProvider);
 | 
						|
  final resp = await client.get('/pass/lotteries?offset=$offset&take=$take');
 | 
						|
  return (resp.data as List).map((e) => SnLotteryTicket.fromJson(e)).toList();
 | 
						|
}
 | 
						|
 | 
						|
@riverpod
 | 
						|
Future<List<SnLotteryRecord>> lotteryRecords(
 | 
						|
  Ref ref, {
 | 
						|
  int offset = 0,
 | 
						|
  int take = 20,
 | 
						|
}) async {
 | 
						|
  final client = ref.watch(apiClientProvider);
 | 
						|
  final resp = await client.get(
 | 
						|
    '/pass/lotteries/records?offset=$offset&take=$take',
 | 
						|
  );
 | 
						|
  return (resp.data as List).map((e) => SnLotteryRecord.fromJson(e)).toList();
 | 
						|
}
 | 
						|
 | 
						|
class LotteryTab extends StatelessWidget {
 | 
						|
  const LotteryTab({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    return DefaultTabController(
 | 
						|
      length: 2,
 | 
						|
      child: Column(
 | 
						|
        children: [
 | 
						|
          TabBar(
 | 
						|
            tabs: [Tab(text: 'myTickets'.tr()), Tab(text: 'drawHistory'.tr())],
 | 
						|
          ),
 | 
						|
          Expanded(
 | 
						|
            child: TabBarView(
 | 
						|
              children: [LotteryTicketsList(), LotteryRecordsList()],
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class LotteryTicketsList extends HookConsumerWidget {
 | 
						|
  const LotteryTicketsList({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    final tickets = ref.watch(lotteryTicketsProvider());
 | 
						|
 | 
						|
    return tickets.when(
 | 
						|
      data: (ticketsList) {
 | 
						|
        if (ticketsList.isEmpty) {
 | 
						|
          return Center(
 | 
						|
            child: Column(
 | 
						|
              mainAxisAlignment: MainAxisAlignment.center,
 | 
						|
              children: [
 | 
						|
                Icon(
 | 
						|
                  Symbols.casino,
 | 
						|
                  size: 48,
 | 
						|
                  color: Theme.of(context).colorScheme.outline,
 | 
						|
                ),
 | 
						|
                const Gap(16),
 | 
						|
                Text(
 | 
						|
                  'noLotteryTickets'.tr(),
 | 
						|
                  style: Theme.of(context).textTheme.titleMedium,
 | 
						|
                ),
 | 
						|
                const Gap(8),
 | 
						|
                Text(
 | 
						|
                  'buyYourFirstTicket'.tr(),
 | 
						|
                  style: Theme.of(context).textTheme.bodyMedium?.copyWith(
 | 
						|
                    color: Theme.of(context).colorScheme.onSurfaceVariant,
 | 
						|
                  ),
 | 
						|
                  textAlign: TextAlign.center,
 | 
						|
                ),
 | 
						|
                const Gap(16),
 | 
						|
                FilledButton.icon(
 | 
						|
                  onPressed: () => _showLotteryPurchaseSheet(context, ref),
 | 
						|
                  icon: const Icon(Symbols.add),
 | 
						|
                  label: Text('buyTicket'.tr()),
 | 
						|
                ),
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
          );
 | 
						|
        }
 | 
						|
 | 
						|
        return Column(
 | 
						|
          children: [
 | 
						|
            Expanded(
 | 
						|
              child: ListView.builder(
 | 
						|
                padding: const EdgeInsets.all(16),
 | 
						|
                itemCount: ticketsList.length,
 | 
						|
                itemBuilder: (context, index) {
 | 
						|
                  final ticket = ticketsList[index];
 | 
						|
                  return Card(
 | 
						|
                    margin: const EdgeInsets.only(bottom: 8),
 | 
						|
                    child: Padding(
 | 
						|
                      padding: const EdgeInsets.all(16),
 | 
						|
                      child: Column(
 | 
						|
                        crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
                        children: [
 | 
						|
                          Row(
 | 
						|
                            children: [
 | 
						|
                              Icon(
 | 
						|
                                Symbols.confirmation_number,
 | 
						|
                                color: Theme.of(context).colorScheme.primary,
 | 
						|
                              ),
 | 
						|
                              const Gap(8),
 | 
						|
                              Expanded(
 | 
						|
                                child: Text(ticket.createdAt.formatSystem()),
 | 
						|
                              ),
 | 
						|
                              Container(
 | 
						|
                                padding: const EdgeInsets.symmetric(
 | 
						|
                                  horizontal: 8,
 | 
						|
                                  vertical: 4,
 | 
						|
                                ),
 | 
						|
                                decoration: BoxDecoration(
 | 
						|
                                  color: _getLotteryStatusColor(
 | 
						|
                                    context,
 | 
						|
                                    ticket.drawStatus,
 | 
						|
                                  ).withOpacity(0.1),
 | 
						|
                                  borderRadius: BorderRadius.circular(12),
 | 
						|
                                ),
 | 
						|
                                child: Text(
 | 
						|
                                  _getLotteryStatusText(ticket.drawStatus),
 | 
						|
                                  style: TextStyle(
 | 
						|
                                    color: _getLotteryStatusColor(
 | 
						|
                                      context,
 | 
						|
                                      ticket.drawStatus,
 | 
						|
                                    ),
 | 
						|
                                    fontSize: 12,
 | 
						|
                                    fontWeight: FontWeight.w600,
 | 
						|
                                  ),
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ],
 | 
						|
                          ),
 | 
						|
                          const Gap(8),
 | 
						|
                          _buildTicketNumbersDisplay(context, ticket),
 | 
						|
                          const Gap(8),
 | 
						|
                          Row(
 | 
						|
                            spacing: 6,
 | 
						|
                            children: [
 | 
						|
                              const Icon(Symbols.asterisk, size: 18),
 | 
						|
                              Text('multiplier').tr().fontSize(13),
 | 
						|
                              Text(
 | 
						|
                                '·',
 | 
						|
                              ).fontWeight(FontWeight.w900).fontSize(13),
 | 
						|
                              Text(
 | 
						|
                                '${ticket.multiplier}x',
 | 
						|
                                style: Theme.of(context).textTheme.bodyMedium,
 | 
						|
                              ).fontSize(13),
 | 
						|
                            ],
 | 
						|
                          ).opacity(0.75),
 | 
						|
                        ],
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                  );
 | 
						|
                },
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
            Padding(
 | 
						|
              padding: const EdgeInsets.all(16),
 | 
						|
              child: FilledButton.icon(
 | 
						|
                onPressed: () => _showLotteryPurchaseSheet(context, ref),
 | 
						|
                icon: const Icon(Symbols.add),
 | 
						|
                label: Text('buyTicket'.tr()),
 | 
						|
                style: FilledButton.styleFrom(
 | 
						|
                  minimumSize: const Size(double.infinity, 48),
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ],
 | 
						|
        );
 | 
						|
      },
 | 
						|
      loading: () => const Center(child: CircularProgressIndicator()),
 | 
						|
      error: (error, stack) => Center(child: Text('Error: $error')),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Future<void> _showLotteryPurchaseSheet(
 | 
						|
    BuildContext context,
 | 
						|
    WidgetRef ref,
 | 
						|
  ) async {
 | 
						|
    final result = await showModalBottomSheet<Map<String, dynamic>>(
 | 
						|
      context: context,
 | 
						|
      useRootNavigator: true,
 | 
						|
      isScrollControlled: true,
 | 
						|
      builder: (context) => const LotteryPurchaseSheet(),
 | 
						|
    );
 | 
						|
 | 
						|
    if (result != null && context.mounted) {
 | 
						|
      await _handleLotteryPurchase(context, ref, result);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Future<void> _handleLotteryPurchase(
 | 
						|
    BuildContext context,
 | 
						|
    WidgetRef ref,
 | 
						|
    Map<String, dynamic> purchaseData,
 | 
						|
  ) async {
 | 
						|
    final client = ref.read(apiClientProvider);
 | 
						|
    try {
 | 
						|
      showLoadingModal(context);
 | 
						|
 | 
						|
      // The lottery API creates the order for us
 | 
						|
      final orderResponse = await client.post(
 | 
						|
        '/pass/lotteries',
 | 
						|
        data: purchaseData,
 | 
						|
      );
 | 
						|
 | 
						|
      if (context.mounted) hideLoadingModal(context);
 | 
						|
 | 
						|
      final order = SnWalletOrder.fromJson(orderResponse.data);
 | 
						|
 | 
						|
      // Show payment overlay
 | 
						|
      if (context.mounted) {
 | 
						|
        final completedOrder = await PaymentOverlay.show(
 | 
						|
          context: context,
 | 
						|
          order: order,
 | 
						|
        );
 | 
						|
 | 
						|
        if (completedOrder != null) {
 | 
						|
          // Payment successful, refresh data
 | 
						|
          ref.invalidate(lotteryTicketsProvider);
 | 
						|
          ref.invalidate(walletCurrentProvider);
 | 
						|
          if (context.mounted) {
 | 
						|
            showSnackBar('ticketPurchasedSuccessfully'.tr());
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } catch (err) {
 | 
						|
      if (context.mounted) hideLoadingModal(context);
 | 
						|
      showErrorAlert(err);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  String _getLotteryStatusText(int status) {
 | 
						|
    switch (status) {
 | 
						|
      case 0:
 | 
						|
        return 'pending'.tr();
 | 
						|
      case 1:
 | 
						|
        return 'drawn'.tr();
 | 
						|
      case 2:
 | 
						|
        return 'won'.tr();
 | 
						|
      case 3:
 | 
						|
        return 'lost'.tr();
 | 
						|
      default:
 | 
						|
        return 'unknown'.tr();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Color _getLotteryStatusColor(BuildContext context, int status) {
 | 
						|
    switch (status) {
 | 
						|
      case 0:
 | 
						|
        return Colors.blue;
 | 
						|
      case 1:
 | 
						|
        return Colors.orange;
 | 
						|
      case 2:
 | 
						|
        return Colors.green;
 | 
						|
      case 3:
 | 
						|
        return Colors.red;
 | 
						|
      default:
 | 
						|
        return Theme.of(context).colorScheme.primary;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Widget _buildTicketNumbersDisplay(
 | 
						|
    BuildContext context,
 | 
						|
    SnLotteryTicket ticket,
 | 
						|
  ) {
 | 
						|
    final numbers = <Widget>[];
 | 
						|
 | 
						|
    // Check if any numbers matched
 | 
						|
    bool hasAnyMatch =
 | 
						|
        ticket.matchedRegionOneNumbers != null &&
 | 
						|
        ticket.matchedRegionOneNumbers!.isNotEmpty;
 | 
						|
 | 
						|
    // Add region one numbers
 | 
						|
    for (final number in ticket.regionOneNumbers) {
 | 
						|
      final isMatched =
 | 
						|
          ticket.matchedRegionOneNumbers?.contains(number) ?? false;
 | 
						|
      if (isMatched) hasAnyMatch = true;
 | 
						|
      numbers.add(
 | 
						|
        _buildNumberWidget(
 | 
						|
          context,
 | 
						|
          number,
 | 
						|
          isMatched: isMatched,
 | 
						|
          allUnmatched: !hasAnyMatch && ticket.drawStatus >= 1,
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    // Add region two number
 | 
						|
    final isSpecialMatched =
 | 
						|
        ticket.matchedRegionTwoNumber == ticket.regionTwoNumber;
 | 
						|
    if (isSpecialMatched) hasAnyMatch = true;
 | 
						|
    numbers.add(
 | 
						|
      _buildNumberWidget(
 | 
						|
        context,
 | 
						|
        ticket.regionTwoNumber,
 | 
						|
        isMatched: isSpecialMatched,
 | 
						|
        isSpecial: true,
 | 
						|
        allUnmatched: !hasAnyMatch && ticket.drawStatus >= 1,
 | 
						|
      ),
 | 
						|
    );
 | 
						|
 | 
						|
    return Wrap(
 | 
						|
      spacing: 6,
 | 
						|
      crossAxisAlignment: WrapCrossAlignment.center,
 | 
						|
      children: numbers,
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Widget _buildNumberWidget(
 | 
						|
    BuildContext context,
 | 
						|
    int number, {
 | 
						|
    bool isMatched = false,
 | 
						|
    bool isSpecial = false,
 | 
						|
    bool allUnmatched = false,
 | 
						|
  }) {
 | 
						|
    Color backgroundColor;
 | 
						|
    Color textColor;
 | 
						|
    Color borderColor;
 | 
						|
 | 
						|
    if (isMatched) {
 | 
						|
      backgroundColor = Colors.green;
 | 
						|
      textColor = Colors.white;
 | 
						|
      borderColor = Colors.green;
 | 
						|
    } else {
 | 
						|
      backgroundColor =
 | 
						|
          isSpecial
 | 
						|
              ? Theme.of(context).colorScheme.secondary
 | 
						|
              : Theme.of(context).colorScheme.surface;
 | 
						|
      textColor =
 | 
						|
          isSpecial
 | 
						|
              ? Theme.of(context).colorScheme.onSecondary
 | 
						|
              : Theme.of(context).colorScheme.onSurface;
 | 
						|
      borderColor =
 | 
						|
          isSpecial
 | 
						|
              ? Theme.of(context).colorScheme.secondary
 | 
						|
              : Theme.of(context).colorScheme.outline.withOpacity(0.3);
 | 
						|
 | 
						|
      // Blend with red if all numbers are unmatched
 | 
						|
      if (allUnmatched) {
 | 
						|
        backgroundColor = Color.alphaBlend(
 | 
						|
          Colors.red.withOpacity(0.3),
 | 
						|
          backgroundColor,
 | 
						|
        );
 | 
						|
        if (!isSpecial) {
 | 
						|
          textColor = Color.alphaBlend(Colors.red.withOpacity(0.5), textColor);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return Container(
 | 
						|
      width: 32,
 | 
						|
      height: 32,
 | 
						|
      decoration: BoxDecoration(
 | 
						|
        color: backgroundColor,
 | 
						|
        border: Border.all(color: borderColor),
 | 
						|
        borderRadius: BorderRadius.circular(8),
 | 
						|
      ),
 | 
						|
      child: Center(
 | 
						|
        child: Text(
 | 
						|
          number.toString().padLeft(2, '0'),
 | 
						|
          style: TextStyle(
 | 
						|
            color: textColor,
 | 
						|
            fontWeight: FontWeight.w600,
 | 
						|
            fontSize: 12,
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class LotteryRecordsList extends HookConsumerWidget {
 | 
						|
  const LotteryRecordsList({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    final records = ref.watch(lotteryRecordsProvider());
 | 
						|
 | 
						|
    return records.when(
 | 
						|
      data: (recordsList) {
 | 
						|
        if (recordsList.isEmpty) {
 | 
						|
          return Center(
 | 
						|
            child: Column(
 | 
						|
              mainAxisAlignment: MainAxisAlignment.center,
 | 
						|
              children: [
 | 
						|
                Icon(
 | 
						|
                  Symbols.history,
 | 
						|
                  size: 48,
 | 
						|
                  color: Theme.of(context).colorScheme.outline,
 | 
						|
                ),
 | 
						|
                const Gap(16),
 | 
						|
                Text(
 | 
						|
                  'noDrawHistory'.tr(),
 | 
						|
                  style: Theme.of(context).textTheme.titleMedium,
 | 
						|
                ),
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
          );
 | 
						|
        }
 | 
						|
 | 
						|
        return ListView.builder(
 | 
						|
          padding: const EdgeInsets.all(16),
 | 
						|
          itemCount: recordsList.length,
 | 
						|
          itemBuilder: (context, index) {
 | 
						|
            final record = recordsList[index];
 | 
						|
            return Card(
 | 
						|
              margin: const EdgeInsets.only(bottom: 8),
 | 
						|
              child: Padding(
 | 
						|
                padding: const EdgeInsets.all(16),
 | 
						|
                child: Column(
 | 
						|
                  crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
                  children: [
 | 
						|
                    Row(
 | 
						|
                      children: [
 | 
						|
                        Icon(
 | 
						|
                          Symbols.celebration,
 | 
						|
                          color: Theme.of(context).colorScheme.primary,
 | 
						|
                        ),
 | 
						|
                        const Gap(8),
 | 
						|
                        Text(
 | 
						|
                          DateFormat.yMd().format(record.drawDate),
 | 
						|
                          style: const TextStyle(
 | 
						|
                            fontSize: 16,
 | 
						|
                            fontWeight: FontWeight.bold,
 | 
						|
                          ),
 | 
						|
                        ),
 | 
						|
                      ],
 | 
						|
                    ),
 | 
						|
                    const Gap(8),
 | 
						|
                    Text(
 | 
						|
                      '${'winningNumbers'.tr()}: ${record.winningRegionOneNumbers.join(', ')}',
 | 
						|
                      style: Theme.of(context).textTheme.bodyMedium,
 | 
						|
                    ),
 | 
						|
                    const Gap(4),
 | 
						|
                    Text(
 | 
						|
                      '${'specialNumber'.tr()}: ${record.winningRegionTwoNumber}',
 | 
						|
                      style: Theme.of(context).textTheme.bodyMedium,
 | 
						|
                    ),
 | 
						|
                    const Gap(8),
 | 
						|
                    Text(
 | 
						|
                      '${'totalTickets'.tr()}: ${record.totalTickets}',
 | 
						|
                      style: Theme.of(context).textTheme.bodySmall,
 | 
						|
                    ),
 | 
						|
                    const Gap(4),
 | 
						|
                    Text(
 | 
						|
                      '${'totalWinners'.tr()}: ${record.totalPrizesAwarded}',
 | 
						|
                      style: Theme.of(context).textTheme.bodySmall,
 | 
						|
                    ),
 | 
						|
                    const Gap(4),
 | 
						|
                    Text(
 | 
						|
                      '${'prizePool'.tr()}: ${record.totalPrizeAmount.toStringAsFixed(2)} ${'walletCurrencyShortPoints'.tr()}',
 | 
						|
                      style: Theme.of(context).textTheme.bodySmall,
 | 
						|
                    ),
 | 
						|
                  ],
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
            );
 | 
						|
          },
 | 
						|
        );
 | 
						|
      },
 | 
						|
      loading: () => const Center(child: CircularProgressIndicator()),
 | 
						|
      error: (error, stack) => Center(child: Text('Error: $error')),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class LotteryPurchaseSheet extends StatefulWidget {
 | 
						|
  const LotteryPurchaseSheet({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  State<LotteryPurchaseSheet> createState() => _LotteryPurchaseSheetState();
 | 
						|
}
 | 
						|
 | 
						|
class _LotteryPurchaseSheetState extends State<LotteryPurchaseSheet> {
 | 
						|
  final List<int> selectedNumbers = [];
 | 
						|
  int multiplier = 1;
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    final totalCost = 10.0 * multiplier; // Base cost of 10 ISP per ticket
 | 
						|
 | 
						|
    return SheetScaffold(
 | 
						|
      titleText: 'buyLotteryTicket'.tr(),
 | 
						|
      child: Column(
 | 
						|
        children: [
 | 
						|
          Expanded(
 | 
						|
            child: SingleChildScrollView(
 | 
						|
              padding: const EdgeInsets.all(16),
 | 
						|
              child: Column(
 | 
						|
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
                children: [
 | 
						|
                  // Number Selection Section
 | 
						|
                  Text(
 | 
						|
                    'selectNumbers'.tr(),
 | 
						|
                    style: TextStyle(
 | 
						|
                      fontSize: 16,
 | 
						|
                      fontWeight: FontWeight.w600,
 | 
						|
                      color: Theme.of(context).colorScheme.primary,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                  const Gap(8),
 | 
						|
                  Text(
 | 
						|
                    'select5UniqueNumbers'.tr(),
 | 
						|
                    style: Theme.of(context).textTheme.bodyMedium?.copyWith(
 | 
						|
                      color: Theme.of(context).colorScheme.onSurfaceVariant,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                  const Gap(4),
 | 
						|
                  Text(
 | 
						|
                    'The last selected number will be your special number.',
 | 
						|
                    style: Theme.of(context).textTheme.bodySmall?.copyWith(
 | 
						|
                      color: Theme.of(context).colorScheme.secondary,
 | 
						|
                      fontWeight: FontWeight.w500,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                  const Gap(16),
 | 
						|
 | 
						|
                  // Number Grid
 | 
						|
                  _buildNumberGrid(),
 | 
						|
 | 
						|
                  const Gap(16),
 | 
						|
 | 
						|
                  // Multiplier Section
 | 
						|
                  Text(
 | 
						|
                    'selectMultiplier'.tr(),
 | 
						|
                    style: TextStyle(
 | 
						|
                      fontSize: 16,
 | 
						|
                      fontWeight: FontWeight.w600,
 | 
						|
                      color: Theme.of(context).colorScheme.primary,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                  const Gap(16),
 | 
						|
                  _buildMultiplierSelector(),
 | 
						|
 | 
						|
                  const Gap(16),
 | 
						|
 | 
						|
                  // Cost Summary
 | 
						|
                  Card(
 | 
						|
                    child: Padding(
 | 
						|
                      padding: const EdgeInsets.all(16),
 | 
						|
                      child: Column(
 | 
						|
                        children: [
 | 
						|
                          Row(
 | 
						|
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
						|
                            children: [
 | 
						|
                              Text('baseCost'.tr()),
 | 
						|
                              Text('10.00 ${'walletCurrencyShortPoints'.tr()}'),
 | 
						|
                            ],
 | 
						|
                          ),
 | 
						|
                          if (multiplier > 1) ...[
 | 
						|
                            const Gap(8),
 | 
						|
                            Row(
 | 
						|
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
						|
                              children: [
 | 
						|
                                Text(
 | 
						|
                                  'multiplier'.tr(
 | 
						|
                                    args: [multiplier.toString()],
 | 
						|
                                  ),
 | 
						|
                                ),
 | 
						|
                                Text(
 | 
						|
                                  '+ ${(10.0 * (multiplier - 1)).toStringAsFixed(2)} ${'walletCurrencyShortPoints'.tr()}',
 | 
						|
                                ),
 | 
						|
                              ],
 | 
						|
                            ),
 | 
						|
                          ],
 | 
						|
                          const Divider(),
 | 
						|
                          Row(
 | 
						|
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
						|
                            children: [
 | 
						|
                              Text(
 | 
						|
                                'totalCost'.tr(),
 | 
						|
                                style: const TextStyle(
 | 
						|
                                  fontWeight: FontWeight.bold,
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                              Text(
 | 
						|
                                '${totalCost.toStringAsFixed(2)} ${'walletCurrencyShortPoints'.tr()}',
 | 
						|
                                style: const TextStyle(
 | 
						|
                                  fontWeight: FontWeight.bold,
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ],
 | 
						|
                          ),
 | 
						|
                        ],
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
 | 
						|
                  const Gap(16),
 | 
						|
 | 
						|
                  // Prize Structure
 | 
						|
                  Text(
 | 
						|
                    'prizeStructure'.tr(),
 | 
						|
                    style: TextStyle(
 | 
						|
                      fontSize: 16,
 | 
						|
                      fontWeight: FontWeight.w600,
 | 
						|
                      color: Theme.of(context).colorScheme.primary,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                  const Gap(8),
 | 
						|
                  _buildPrizeStructure(),
 | 
						|
                ],
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
 | 
						|
          // Action Buttons
 | 
						|
          Padding(
 | 
						|
            padding: const EdgeInsets.all(16),
 | 
						|
            child: Row(
 | 
						|
              children: [
 | 
						|
                Expanded(
 | 
						|
                  child: OutlinedButton(
 | 
						|
                    onPressed: () => Navigator.of(context).pop(),
 | 
						|
                    child: Text('cancel'.tr()),
 | 
						|
                  ),
 | 
						|
                ),
 | 
						|
                const Gap(8),
 | 
						|
                Expanded(
 | 
						|
                  child: FilledButton(
 | 
						|
                    onPressed: _canPurchase ? _purchaseTicket : null,
 | 
						|
                    child: Text('purchase'.tr()),
 | 
						|
                  ),
 | 
						|
                ),
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Widget _buildNumberGrid() {
 | 
						|
    return GridView.builder(
 | 
						|
      shrinkWrap: true,
 | 
						|
      physics: const NeverScrollableScrollPhysics(),
 | 
						|
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
 | 
						|
        crossAxisCount: 10,
 | 
						|
        crossAxisSpacing: 8,
 | 
						|
        mainAxisSpacing: 8,
 | 
						|
      ),
 | 
						|
      itemCount: 100,
 | 
						|
      itemBuilder: (context, index) {
 | 
						|
        final number = index;
 | 
						|
        final isSelected = selectedNumbers.contains(number);
 | 
						|
        final isSpecialNumber =
 | 
						|
            selectedNumbers.isNotEmpty &&
 | 
						|
            selectedNumbers.last == number &&
 | 
						|
            selectedNumbers.length == 6;
 | 
						|
 | 
						|
        return GestureDetector(
 | 
						|
          onTap: () => _toggleNumber(number),
 | 
						|
          child: Container(
 | 
						|
            decoration: BoxDecoration(
 | 
						|
              color:
 | 
						|
                  isSpecialNumber
 | 
						|
                      ? Theme.of(context).colorScheme.secondary
 | 
						|
                      : isSelected
 | 
						|
                      ? Theme.of(context).colorScheme.primary
 | 
						|
                      : Theme.of(context).colorScheme.surface,
 | 
						|
              border: Border.all(
 | 
						|
                color:
 | 
						|
                    isSpecialNumber
 | 
						|
                        ? Theme.of(context).colorScheme.secondary
 | 
						|
                        : isSelected
 | 
						|
                        ? Theme.of(context).colorScheme.primary
 | 
						|
                        : Theme.of(
 | 
						|
                          context,
 | 
						|
                        ).colorScheme.outline.withOpacity(0.3),
 | 
						|
              ),
 | 
						|
              borderRadius: BorderRadius.circular(8),
 | 
						|
            ),
 | 
						|
            child: Center(
 | 
						|
              child: Text(
 | 
						|
                number.toString().padLeft(2, '0'),
 | 
						|
                style: TextStyle(
 | 
						|
                  color:
 | 
						|
                      isSpecialNumber
 | 
						|
                          ? Theme.of(context).colorScheme.onSecondary
 | 
						|
                          : isSelected
 | 
						|
                          ? Theme.of(context).colorScheme.onPrimary
 | 
						|
                          : Theme.of(context).colorScheme.onSurface,
 | 
						|
                  fontWeight: FontWeight.w600,
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        );
 | 
						|
      },
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Widget _buildMultiplierSelector() {
 | 
						|
    return TextFormField(
 | 
						|
      initialValue: multiplier.toString(),
 | 
						|
      keyboardType: TextInputType.number,
 | 
						|
      decoration: InputDecoration(
 | 
						|
        labelText: 'multiplierLabel'.tr(),
 | 
						|
        prefixText: 'x',
 | 
						|
        border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
 | 
						|
        contentPadding: const EdgeInsets.symmetric(
 | 
						|
          horizontal: 16,
 | 
						|
          vertical: 12,
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
      onChanged: (value) {
 | 
						|
        final parsed = int.tryParse(value);
 | 
						|
        if (parsed != null && parsed >= 1) {
 | 
						|
          setState(() => multiplier = parsed);
 | 
						|
        }
 | 
						|
      },
 | 
						|
      validator: (value) {
 | 
						|
        if (value == null || value.isEmpty) {
 | 
						|
          return 'Please enter a multiplier';
 | 
						|
        }
 | 
						|
        final parsed = int.tryParse(value);
 | 
						|
        if (parsed == null || parsed < 1 || parsed > 10) {
 | 
						|
          return 'Multiplier must be between 1 and 10';
 | 
						|
        }
 | 
						|
        return null;
 | 
						|
      },
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  Widget _buildPrizeStructure() {
 | 
						|
    // Base rewards for matched numbers (0-5)
 | 
						|
    final baseRewards = [0, 10, 100, 500, 1000, 10000];
 | 
						|
 | 
						|
    final prizeStructure = <String, String>{};
 | 
						|
 | 
						|
    // Generate prize structure for 0-5 matches with and without special
 | 
						|
    for (int matches = 5; matches >= 0; matches--) {
 | 
						|
      final baseReward = baseRewards[matches];
 | 
						|
 | 
						|
      // With special number match (x10 multiplier)
 | 
						|
      final specialReward = baseReward * 10;
 | 
						|
      prizeStructure['$matches+Special'] = specialReward.toStringAsFixed(2);
 | 
						|
 | 
						|
      // Without special number match
 | 
						|
      if (matches > 0) {
 | 
						|
        prizeStructure[matches.toString()] = baseReward.toStringAsFixed(2);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return Card(
 | 
						|
      child: Padding(
 | 
						|
        padding: const EdgeInsets.all(16),
 | 
						|
        child: Column(
 | 
						|
          children:
 | 
						|
              prizeStructure.entries.map((entry) {
 | 
						|
                return Padding(
 | 
						|
                  padding: const EdgeInsets.only(bottom: 8),
 | 
						|
                  child: Row(
 | 
						|
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
						|
                    children: [
 | 
						|
                      Text(
 | 
						|
                        entry.key == '0+Special'
 | 
						|
                            ? 'specialOnly'.tr()
 | 
						|
                            : entry.key.tr(),
 | 
						|
                      ),
 | 
						|
                      Text(
 | 
						|
                        '${entry.value} ${'walletCurrencyShortPoints'.tr()}',
 | 
						|
                      ),
 | 
						|
                    ],
 | 
						|
                  ),
 | 
						|
                );
 | 
						|
              }).toList(),
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  void _toggleNumber(int number) {
 | 
						|
    setState(() {
 | 
						|
      if (selectedNumbers.contains(number)) {
 | 
						|
        selectedNumbers.remove(number);
 | 
						|
      } else if (selectedNumbers.length < 6) {
 | 
						|
        selectedNumbers.add(number);
 | 
						|
      }
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  bool get _canPurchase {
 | 
						|
    return selectedNumbers.length == 6;
 | 
						|
  }
 | 
						|
 | 
						|
  Future<void> _purchaseTicket() async {
 | 
						|
    if (!_canPurchase) return;
 | 
						|
 | 
						|
    // Sort all numbers except the last one (special number)
 | 
						|
    final regularNumbers = selectedNumbers.sublist(0, 5)..sort();
 | 
						|
    final specialNumber = selectedNumbers.last;
 | 
						|
 | 
						|
    final purchaseData = {
 | 
						|
      'region_one_numbers': regularNumbers,
 | 
						|
      'region_two_number': specialNumber,
 | 
						|
      'multiplier': multiplier,
 | 
						|
    };
 | 
						|
 | 
						|
    if (mounted) Navigator.of(context).pop(purchaseData);
 | 
						|
  }
 | 
						|
}
 |