# Developer Guide

## 18.1 Development Setup

### Prerequisites
- **PHP**: 8.2 or higher
- **Node.js**: 18.0 or higher
- **Composer**: 2.0 or higher
- **Git**: Version control
- **Database**: MySQL 8.0+ or SQLite 3
- **Code Editor**: VS Code, PhpStorm, or similar

### Environment Setup
```bash
# Clone repository
git clone <repository-url>
cd the-milimus-tournament

# Install PHP dependencies
composer install

# Install Node.js dependencies
npm install

# Copy environment file
cp .env.example .env

# Generate application key
php artisan key:generate

# Run database migrations
php artisan migrate

# Seed database (optional)
php artisan db:seed

# Build frontend assets
npm run dev
```

### Development Tools
```bash
# Install development dependencies
composer require --dev phpunit/phpunit
composer require --dev pestphp/pest
composer require --dev barryvdh/laravel-debugbar

# Install frontend development tools
npm install --save-dev @types/react
npm install --save-dev @types/react-dom
npm install --save-dev typescript
```

## 18.2 Code Standards

### PHP Code Standards
- **PSR-12**: Follow PSR-12 coding standards
- **Laravel Conventions**: Follow Laravel naming conventions
- **Documentation**: Document all public methods
- **Type Hints**: Use type hints for parameters and return types
- **Error Handling**: Implement proper error handling

### Code Style Example
```php
<?php

namespace App\Services;

use App\Models\Tournament;
use App\Models\Bracket;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class TournamentBracketService
{
    protected Tournament $tournament;
    protected array $participants;
    protected ?string $format;
    protected array $settings;

    public function __construct(Tournament $tournament)
    {
        $this->tournament = $tournament;
        $this->format = $tournament->format ?? 'team';
        $this->settings = $tournament->bracket_settings ?? [];
    }

    /**
     * Generate bracket based on tournament type
     *
     * @param array $participants
     * @param string $bracketType
     * @return Bracket
     * @throws \Exception
     */
    public function generateBracket(array $participants, string $bracketType = 'winners'): Bracket
    {
        try {
            $this->participants = $participants;
            
            $bracket = Bracket::create([
                'tournament_id' => $this->tournament->id,
                'name' => $this->getBracketName($bracketType),
                'type' => $bracketType,
                'is_active' => true,
            ]);

            $seededParticipants = $this->applySeeding($participants);
            $this->generateMatches($bracket, $seededParticipants);

            return $bracket->fresh(['gameMatches']);
            
        } catch (\Exception $e) {
            Log::error('Bracket generation failed: ' . $e->getMessage());
            throw $e;
        }
    }
}
```

### TypeScript Code Standards
- **TypeScript**: Use TypeScript for all React components
- **Interface Definitions**: Define interfaces for all props and data
- **Component Structure**: Follow React best practices
- **Error Handling**: Implement proper error boundaries
- **Performance**: Use React optimization techniques

### TypeScript Example
```typescript
interface TournamentProps {
  tournament: Tournament;
  onUpdate: (tournament: Tournament) => void;
  onDelete: (id: number) => void;
}

interface Tournament {
  id: number;
  name: string;
  status: 'draft' | 'active' | 'completed' | 'cancelled';
  start_date: string;
  end_date: string;
  max_participants: number;
  entry_fee: number;
}

const TournamentCard: React.FC<TournamentProps> = ({ 
  tournament, 
  onUpdate, 
  onDelete 
}) => {
  const [loading, setLoading] = useState(false);

  const handleUpdate = async (updatedTournament: Tournament) => {
    setLoading(true);
    try {
      await onUpdate(updatedTournament);
    } catch (error) {
      console.error('Update failed:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="tournament-card">
      <h3>{tournament.name}</h3>
      <p>Status: {tournament.status}</p>
      <p>Start Date: {tournament.start_date}</p>
      <p>Entry Fee: KES {tournament.entry_fee}</p>
      <button 
        onClick={() => handleUpdate(tournament)}
        disabled={loading}
      >
        {loading ? 'Updating...' : 'Update'}
      </button>
    </div>
  );
};
```

## 18.3 Contributing

### Development Workflow
1. **Fork Repository**: Fork the main repository
2. **Create Branch**: Create feature branch from main
3. **Make Changes**: Implement your changes
4. **Write Tests**: Add tests for new functionality
5. **Update Documentation**: Update relevant documentation
6. **Submit Pull Request**: Submit PR for review

### Branch Naming Convention
```
feature/tournament-bracket-generation
bugfix/payment-processing-error
hotfix/security-vulnerability
docs/api-documentation-update
```

### Commit Message Format
```
feat: add tournament bracket generation
fix: resolve payment processing error
docs: update API documentation
test: add unit tests for bracket service
refactor: improve code structure
```

### Pull Request Guidelines
- **Clear Description**: Describe what the PR does
- **Testing**: Include test results
- **Documentation**: Update relevant documentation
- **Code Review**: Address review comments
- **Merge Conflicts**: Resolve any conflicts

## 18.4 Debugging

### Laravel Debugging
```php
// Debug bar (development only)
composer require barryvdh/laravel-debugbar

// Logging
Log::info('Debug message', ['data' => $data]);
Log::error('Error message', ['exception' => $exception]);

// Database query debugging
DB::enableQueryLog();
// ... your code ...
$queries = DB::getQueryLog();
dd($queries);

// Tinker for interactive debugging
php artisan tinker
```

### React Debugging
```typescript
// Console logging
console.log('Debug data:', data);
console.error('Error:', error);

// React DevTools
// Install React DevTools browser extension

// Error boundaries
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}
```

### Database Debugging
```sql
-- Enable query logging
SET GLOBAL general_log = 'ON';
SET GLOBAL general_log_file = '/var/log/mysql/general.log';

-- View slow queries
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';

-- Analyze query performance
EXPLAIN SELECT * FROM tournaments WHERE status = 'active';
```

## 18.5 Performance Optimization

### Laravel Optimization
```php
// Database optimization
// Use eager loading to prevent N+1 queries
$tournaments = Tournament::with(['teams', 'brackets'])->get();

// Use database indexes
Schema::table('tournaments', function (Blueprint $table) {
    $table->index(['status', 'start_date']);
    $table->index('organizer_id');
});

// Cache frequently accessed data
Cache::remember('tournaments.active', 3600, function () {
    return Tournament::where('status', 'active')->get();
});

// Use database transactions for bulk operations
DB::transaction(function () {
    // Multiple database operations
});
```

### React Optimization
```typescript
// Memoization for expensive calculations
const expensiveValue = useMemo(() => {
  return calculateExpensiveValue(data);
}, [data]);

// Callback memoization
const handleClick = useCallback(() => {
  // Handle click
}, [dependency]);

// Component memoization
const MemoizedComponent = React.memo(({ data }) => {
  return <div>{data}</div>;
});

// Lazy loading
const LazyComponent = React.lazy(() => import('./LazyComponent'));

// Virtual scrolling for large lists
import { FixedSizeList as List } from 'react-window';
```

### Frontend Optimization
```typescript
// Code splitting
const TournamentPage = React.lazy(() => import('./TournamentPage'));

// Image optimization
<img 
  src={optimizedImageUrl} 
  alt="Tournament" 
  loading="lazy"
  width={300}
  height={200}
/>

// Bundle optimization
// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu']
        }
      }
    }
  }
});
```

## 18.6 Extending the System

### Adding New Features
1. **Create Models**: Define new data models
2. **Create Migrations**: Database schema changes
3. **Create Services**: Business logic implementation
4. **Create Controllers**: API endpoints
5. **Create Frontend**: React components
6. **Add Tests**: Unit and integration tests
7. **Update Documentation**: Update relevant docs

### Custom Service Example
```php
<?php

namespace App\Services;

use App\Models\Tournament;
use App\Models\Team;
use Illuminate\Support\Facades\Log;

class CustomTournamentService
{
    protected Tournament $tournament;

    public function __construct(Tournament $tournament)
    {
        $this->tournament = $tournament;
    }

    /**
     * Custom tournament feature
     */
    public function customFeature(): array
    {
        try {
            // Implementation
            return ['success' => true, 'data' => $result];
        } catch (\Exception $e) {
            Log::error('Custom feature failed: ' . $e->getMessage());
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }
}
```

### Custom React Component
```typescript
import React, { useState, useEffect } from 'react';

interface CustomComponentProps {
  data: any[];
  onUpdate: (data: any) => void;
}

const CustomComponent: React.FC<CustomComponentProps> = ({ 
  data, 
  onUpdate 
}) => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    // Component initialization
  }, []);

  const handleUpdate = async (updatedData: any) => {
    setLoading(true);
    setError(null);
    
    try {
      await onUpdate(updatedData);
    } catch (err) {
      setError('Update failed');
    } finally {
      setLoading(false);
    }
  };

  if (error) {
    return <div className="error">{error}</div>;
  }

  return (
    <div className="custom-component">
      {loading && <div>Loading...</div>}
      {/* Component content */}
    </div>
  );
};

export default CustomComponent;
```

### Database Extensions
```php
// Migration for new feature
Schema::create('custom_feature', function (Blueprint $table) {
    $table->id();
    $table->foreignId('tournament_id')->constrained();
    $table->string('name');
    $table->json('settings');
    $table->timestamps();
    
    $table->index(['tournament_id', 'name']);
});

// Model for new feature
class CustomFeature extends Model
{
    protected $fillable = ['tournament_id', 'name', 'settings'];
    
    public function tournament() { return $this->belongsTo(Tournament::class); }
}
```

---

## Development Best Practices

### Code Quality
- **Write Tests**: Always write tests for new features
- **Document Code**: Document all public methods and classes
- **Follow Standards**: Adhere to coding standards
- **Error Handling**: Implement proper error handling
- **Performance**: Consider performance implications

### Security
- **Input Validation**: Validate all user inputs
- **SQL Injection**: Use parameterized queries
- **XSS Prevention**: Sanitize output data
- **CSRF Protection**: Use CSRF tokens
- **Authentication**: Implement proper authentication

### Testing
- **Unit Tests**: Test individual components
- **Integration Tests**: Test component interactions
- **Feature Tests**: Test complete user workflows
- **Performance Tests**: Test system performance
- **Security Tests**: Test security measures

---

## 18.7 Location Management System

### Configuration Structure

The system uses a category-based location management approach defined in `config/tournament.php`:

```php
// Global location settings (applies to unrestricted categories)
'available_countries' => env('TOURNAMENT_COUNTRIES', 'Kenya'),
'available_counties' => env('TOURNAMENT_COUNTIES', 'Kakamega County,Nairobi County,Mombasa County'),

// Category-specific location restrictions
'category_locations' => [
    'men' => [
        'restrict_locations' => false,  // Toggle restriction ON/OFF
        'locations' => [],               // Array of allowed locations
    ],
    'women' => [
        'restrict_locations' => true,
        'locations' => ['Nairobi', 'Mombasa'],
    ],
    // ... other categories: youth_male, youth_female, children
]
```

### Backend Implementation

#### Controller: TournamentSettingsController

**Get Settings:**
```php
private function getCurrentSettings(): array
{
    return [
        // ... other settings
        'available_countries' => config('tournament.available_countries', 'Kenya'),
        'available_counties' => config('tournament.available_counties'),
        'category_locations' => config('tournament.category_locations', []),
        'categories' => config('tournament.categories', []),
    ];
}
```

**Update Settings:**
```php
public function update(Request $request)
{
    // Validate category_locations structure
    $validator = Validator::make($request->all(), [
        'category_locations' => 'nullable|array',
        'category_locations.*.restrict_locations' => 'nullable|boolean',
        'category_locations.*.locations' => 'nullable|array',
    ]);
    
    // Update config file
    if (isset($settings['category_locations'])) {
        $this->updateConfigFile($settings['category_locations']);
    }
    
    // Auto-clear cache
    $this->updateConfigCache();
}
```

#### Registration Controller

```php
public function create(): Response
{
    // Get category locations
    $categoryLocations = config('tournament.category_locations', []);
    $countries = explode(',', config('tournament.available_countries'));
    $counties = explode(',', config('tournament.available_counties'));
    
    return Inertia::render('frontend/auth/RegisterTeam', [
        'category_locations' => $categoryLocations,
        'countries' => $countries,
        'counties' => $counties,
        // ... other data
    ]);
}
```

### Frontend Integration

#### Tournament Settings UI

```tsx
// Category location configuration
{Object.keys(form.categories || {}).map((categoryKey) => {
  const categoryLocation = form.category_locations?.[categoryKey];
  
  return (
    <div key={categoryKey}>
      <Switch
        checked={categoryLocation.restrict_locations}
        onCheckedChange={(checked) => {
          // Update category restriction
        }}
      />
      <TagsInput
        disabled={!categoryLocation.restrict_locations}
        value={categoryLocation.locations.join(', ')}
        onChange={(value) => {
          // Update locations
        }}
      />
    </div>
  );
})}
```

#### Team Registration

```tsx
// Dynamic location input based on category
const categoryLoc = category_locations[data.team.category];
const isRestricted = categoryLoc?.restrict_locations && categoryLoc?.locations?.length > 0;

if (isRestricted) {
  // Show dropdown
  <select>
    {categoryLoc.locations.map(location => (
      <option key={location} value={location}>{location}</option>
    ))}
  </select>
} else {
  // Show text input
  <Input placeholder="Enter your team's location" />
}
```

#### Category Filtering

```tsx
// Show only enabled categories
categories={Object.fromEntries(
  Object.entries(categories)
    .filter(([key]) => {
      return selectedTournament?.available_categories?.includes(key);
    })
    .map(([key, cat]) => [key, { ...cat, fee: tournament.category_fees[key] }])
)}
```

### API Endpoints

**Tournament Settings:**
- `PUT /admin/tournament-settings/update` - Update global settings
  - Auto-clears config cache
  - Returns Inertia redirect

**Team Registration:**
- `GET /register-team` - Registration form
  - Receives: categories, category_locations, countries, counties
- `POST /register-team` - Submit registration
  - Validates: team.category, team.location against restrictions

### Cache Management

The system automatically clears and rebuilds config cache after settings updates:

```php
private function updateConfigCache(): void
{
    \Artisan::call('config:clear');
    \Artisan::call('config:cache');
    
    if (function_exists('opcache_reset')) {
        opcache_reset();
    }
}
```

No manual cache clearing required - changes take effect immediately.

---

## Next Steps

- [API Reference](19_API_Reference.md) - Complete API documentation
- [System Architecture](03_System_Architecture.md) - Technical architecture
- [Testing](15_Testing.md) - Testing strategies and implementation
- [Deployment](14_Deployment.md) - Production deployment guide
