:bug# Lottery System API Documentation

## Overview

The DysonNetwork Lottery System provides a daily lottery where users can purchase tickets with custom number selections. Each day features a new draw with random winning numbers. Users purchase tickets using ISP (Dyson Network Points), with results announced each morning.

The API is handled by the DysonNetwork.Pass service. Which means if you use it with the Gateway the `/api` should be replaced with `/pass`

### Key Features

- **Daily Draws**: Automated draws at midnight UTC
- **Custom Number Selection**: Users choose 5 unique numbers (0-99) + 1 special number (0-99)
- **Flexible Pricing**: Base cost 10 ISP + extra ISP per multiplier (e.g., multiplier=2 costs 20 ISP)
- **Daily Limits**: One ticket purchase per user per day
- **Prize System**: Multiple prize tiers based on matches
- **Instant Payment**: Tickets purchased using in-app points
- **Historical Records**: Complete draw history and statistics

## Data Models

### LotteryDrawStatus Enum
```csharp
public enum LotteryDrawStatus
{
    Pending = 0,    // Ticket awaiting draw
    Drawn = 1       // Ticket has been processed in draw
}
```

### SnLottery Model
```csharp
public class SnLottery : ModelBase
{
    public Guid Id { get; set; }
    public SnAccount Account { get; set; } = null!;
    public Guid AccountId { get; set; }
    public List<int> RegionOneNumbers { get; set; } = new(); // 5 numbers (0-99)
    public int RegionTwoNumber { get; set; }                    // Special number (0-99)
    public int Multiplier { get; set; } = 1;                     // Prize multiplier (≥1)
    public LotteryDrawStatus DrawStatus { get; set; }
    public DateTime? DrawDate { get; set; }                      // Date when drawn
}
```

### SnLotteryRecord Model
```csharp
public class SnLotteryRecord : ModelBase
{
    public Guid Id { get; set; }
    public DateTime DrawDate { get; set; }
    public List<int> WinningRegionOneNumbers { get; set; } = new(); // 5 winning numbers
    public int WinningRegionTwoNumber { get; set; }                   // Winning special number
    public int TotalTickets { get; set; }                             // Total tickets processed
    public int TotalPrizesAwarded { get; set; }                       // Number of winning tickets
    public long TotalPrizeAmount { get; set; }                        // Total ISP prize amount
}
```

## Prize Structure

| Region 1 Matches | Base Prize (ISP) | Notes |
|-----------------|------------------|-------|
| 0 | 0 | No prize |
| 1 | 10 | Minimum win |
| 2 | 20 | Double minimum |
| 3 | 50 | Five times minimum |
| 4 | 100 | Ten times minimum |
| 5 | 1000 | Maximum prize |

**Special Number Bonus**: If Region 2 number matches, multiply any prize by 10x.

## API Endpoints

All endpoints require authentication via Bearer token.

### Purchase Ticket
**POST** `/api/lotteries`

Creates a lottery order and deducts ISP from user's wallet.

**Request Body:**
```json
{
  "RegionOneNumbers": [5, 23, 47, 68, 89],
  "RegionTwoNumber": 42,
  "Multiplier": 1
}
```

**Response:**
```json
{
  "id": "guid",
  "accountId": "guid",
  "createdAt": "2025-10-24T00:00:00Z",
  "status": "Paid",
  "currency": "isp",
  "amount": 10,
  "productIdentifier": "lottery"
}
```

**Validation Rules:**
- `RegionOneNumbers`: Exactly 5 unique integers between 0-99
- `RegionTwoNumber`: Single integer between 0-99
- `Multiplier`: Integer ≥ 1
- User can only purchase 1 ticket per day

**Pricing:**
- Base cost: 10 ISP
- Additional cost: (Multiplier - 1) × 10 ISP
- Total cost = (Multiplier × 10) ISP

### Get User Tickets
**GET** `/api/lotteries`

Retrieves user's lottery tickets with pagination.

**Query Parameters:**
- `offset` (optional, default 0): Page offset
- `limit` (optional, default 20, max 100): Items per page

**Response:**
```json
[
  {
    "id": "guid",
    "regionOneNumbers": [5, 23, 47, 68, 89],
    "regionTwoNumber": 42,
    "multiplier": 1,
    "drawStatus": "Pending",
    "drawDate": null,
    "createdAt": "2025-10-24T10:30:00Z"
  }
]
```

**Response Headers:**
```
X-Total: 42  // Total number of user's tickets
```

### Get Specific Ticket
**GET** `/api/lotteries/{id}`

Retrieves a specific lottery ticket by ID.

**Response:**
Same structure as individual items from Get User Tickets.

**Error Responses:**
- `404 Not Found`: Ticket doesn't exist or user doesn't own it

### Get Lottery Records
**GET** `/api/lotteries/records`

Retrieves historical lottery draw results.

**Query Parameters:**
- `startDate` (optional): Filter by draw date (YYYY-MM-DD)
- `endDate` (optional): Filter by draw date (YYYY-MM-DD)
- `offset` (optional, default 0): Page offset
- `limit` (optional, default 20): Items per page

**Response:**
```json
[
  {
    "id": "guid",
    "drawDate": "2025-10-24T00:00:00Z",
    "winningRegionOneNumbers": [7, 15, 23, 46, 82],
    "winningRegionTwoNumber": 19,
    "totalTickets": 245,
    "totalPrizesAwarded": 23,
    "totalPrizeAmount": 4820
  }
]
```

## Integration Examples

### Frontend Integration (JavaScript/React)

```javascript
// Purchase a lottery ticket
async function purchaseLottery(numbers, specialNumber, multiplier = 1) {
  try {
    const response = await fetch('/api/lotteries', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${userToken}`
      },
      body: JSON.stringify({
        RegionOneNumbers: numbers,      // Array of 5 unique numbers 0-99
        RegionTwoNumber: specialNumber, // Number 0-99
        Multiplier: multiplier          // Optional, defaults to 1
      })
    });

    const order = await response.json();

    if (response.ok) {
      console.log('Ticket purchased successfully!', order);
      // Refresh user ISP balance
      updateWalletBalance();
    } else {
      console.error('Purchase failed:', order);
    }
  } catch (error) {
    console.error('Network error:', error);
  }
}

// Get user's tickets
async function getUserTickets() {
  try {
    const response = await fetch('/api/lotteries?limit=20', {
      headers: {
        'Authorization': `Bearer ${userToken}`
      }
    });

    const tickets = await response.json();
    const totalTickets = response.headers.get('X-Total');

    return { tickets, total: parseInt(totalTickets) };
  } catch (error) {
    console.error('Error fetching tickets:', error);
  }
}

// Get draw history
async function getDrawHistory() {
  try {
    const response = await fetch('/api/lotteries/records', {
      headers: {
        'Authorization': `Bearer ${userToken}`
      }
    });

    return await response.json();
  } catch (error) {
    console.error('Error fetching history:', error);
  }
}
```

### Mobile Integration (React Native/TypeScript)

```typescript
interface LotteryTicket {
  id: string;
  regionOneNumbers: number[];
  regionTwoNumber: number;
  multiplier: number;
  drawStatus: 'Pending' | 'Drawn';
  drawDate?: string;
  createdAt: string;
}

interface PurchaseRequest {
  RegionOneNumbers: number[];
  RegionTwoNumber: number;
  Multiplier: number;
}

class LotteryService {
  private apiUrl = 'https://your-api-domain.com/api/lotteries';

  async purchaseTicket(
    ticket: Omit<PurchaseRequest, 'RegionOneNumbers'> & { numbers: number[] },
    token: string
  ): Promise<any> {
    const request: PurchaseRequest = {
      RegionOneNumbers: ticket.numbers,
      RegionTwoNumber: ticket.RegionTwoNumber,
      Multiplier: ticket.Multiplier
    };

    const response = await fetch(this.apiUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify(request)
    });

    return response.json();
  }

  async getTickets(token: string, offset = 0, limit = 20): Promise<LotteryTicket[]> {
    const response = await fetch(`${this.apiUrl}?offset=${offset}&limit=${limit}`, {
      headers: { 'Authorization': `Bearer ${token}` }
    });

    return response.json();
  }

  async getDrawRecords(token: string): Promise<any[]> {
    const response = await fetch(`${this.apiUrl}/records`, {
      headers: { 'Authorization': `Bearer ${token}` }
    });

    return response.json();
  }
}
```

### Number Validation

```javascript
function validateLotteryNumbers(numbers, specialNumber, multiplier = 1) {
  // Validate region one numbers
  if (!Array.isArray(numbers) || numbers.length !== 5) {
    return { valid: false, error: 'Must select exactly 5 numbers' };
  }

  const uniqueNumbers = new Set(numbers);
  if (uniqueNumbers.size !== 5) {
    return { valid: false, error: 'Numbers must be unique' };
  }

  // Check range 0-99
  for (const num of numbers) {
    if (!Number.isInteger(num) || num < 0 || num > 99) {
      return { valid: false, error: 'Numbers must be integers between 0-99' };
    }
  }

  // Validate special number
  if (!Number.isInteger(specialNumber) || specialNumber < 0 || specialNumber > 99) {
    return { valid: false, error: 'Special number must be between 0-99' };
  }

  // Validate multiplier
  if (!Number.isInteger(multiplier) || multiplier < 1) {
    return { valid: false, error: 'Multiplier must be 1 or greater' };
  }

  return { valid: true };
}

// Example usage
const validation = validateLotteryNumbers([5, 12, 23, 47, 89], 42, 2);
if (!validation.valid) {
  console.error(validation.error);
}
```

## Daily Draw Schedule

- **Draw Time**: Every midnight UTC (00:00 UTC)
- **Processing**: Only tickets from the previous day are included
- **Prize Distribution**: Winners automatically receive ISP credits
- **History**: Draws are preserved indefinitely

## Error Handling

### Common Error Codes
- `400 Bad Request`: Invalid request data (bad numbers, duplicate purchase, etc.)
- `401 Unauthorized`: Missing or invalid authentication token
- `404 Not Found`: Ticket doesn't exist or access denied
- `403 Forbidden`: Insufficient permissions (admin endpoints)

### Error Response Format
```json
{
  "message": "You can only purchase one lottery per day.",
  "type": "ArgumentException",
  "statusCode": 400
}
```

## Testing Guidelines

### Test Cases
1. **Valid Purchase**: Select valid numbers, verify wallet deduction
2. **Invalid Numbers**: Try duplicate region one numbers, out-of-range values
3. **Daily Limit**: Attempt second purchase in same day
4. **Insufficient Funds**: Try purchase without enough ISP
5. **Draw Processing**: Verify winning tickets receive correct prizes
6. **Historical Data**: Check draw records match processed tickets

### Test Data Examples
```javascript
// Valid ticket
{ numbers: [1, 15, 23, 67, 89], special: 42, multiplier: 1 }

// Invalid - duplicate numbers
{ numbers: [1, 15, 23, 15, 89], special: 42, multiplier: 1 }

// Invalid - out of range
{ numbers: [1, 15, 23, 67, 150], special: 42, multiplier: 1 }
```

## Support

For API integration questions or support:
- Check network documentation for authentication details
- Contact Dyson Network development team for assistance
- Monitor API response headers for pagination metadata
