# Performance Optimization Guide

This guide provides comprehensive strategies to optimize application performance.

---

## 1. Frontend Performance

### 1.1 Code Splitting (IMPLEMENTED)

The `App.lazy.tsx` file implements route-based code splitting. To use it:

```typescript
// frontend/src/index.tsx
import AppLazy from './App.lazy';

root.render(
  <React.StrictMode>
    <ErrorBoundary>
      <HelmetProvider>
        <ColorModeScript initialColorMode={theme.config.initialColorMode} />
        <AppLazy />
      </HelmetProvider>
    </ErrorBoundary>
  </React.StrictMode>
);
```

**Expected Results**:
- Initial bundle size reduced by 60-70%
- Faster Time to Interactive (TTI)
- Better Lighthouse scores

### 1.2 Image Optimization

#### Server-Side Image Processing

```go
// pkg/utils/image.go
package utils

import (
    "image"
    "image/jpeg"
    "image/png"
    "os"
    
    "github.com/nfnt/resize"
)

type ImageSize struct {
    Width  uint
    Height uint
    Name   string
}

var ThumbnailSizes = []ImageSize{
    {Width: 150, Height: 150, Name: "thumb"},
    {Width: 400, Height: 400, Name: "small"},
    {Width: 800, Height: 800, Name: "medium"},
    {Width: 1200, Height: 0, Name: "large"},
}

func GenerateThumbnails(sourcePath string, outputDir string) (map[string]string, error) {
    file, err := os.Open(sourcePath)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    img, format, err := image.Decode(file)
    if err != nil {
        return nil, err
    }
    
    results := make(map[string]string)
    
    for _, size := range ThumbnailSizes {
        resized := resize.Resize(size.Width, size.Height, img, resize.Lanczos3)
        
        outputPath := fmt.Sprintf("%s/%s_%s.jpg", outputDir, 
            filepath.Base(sourcePath), size.Name)
        
        out, err := os.Create(outputPath)
        if err != nil {
            continue
        }
        
        if format == "png" {
            png.Encode(out, resized)
        } else {
            jpeg.Encode(out, resized, &jpeg.Options{Quality: 85})
        }
        out.Close()
        
        results[size.Name] = outputPath
    }
    
    return results, nil
}
```

#### WebP Conversion

```bash
# Install webp tools
# Ubuntu: sudo apt-get install webp
# Mac: brew install webp

# Convert images
cwebp input.jpg -q 80 -o output.webp
```

#### Frontend: Responsive Images

```tsx
// components/OptimizedImage.tsx
import React from 'react';
import { Box, Image } from '@chakra-ui/react';

interface OptimizedImageProps {
  src: string;
  alt: string;
  sizes?: string;
}

export const OptimizedImage: React.FC<OptimizedImageProps> = ({ src, alt, sizes }) => {
  const basePath = src.replace(/\.[^.]+$/, '');
  
  return (
    <picture>
      {/* WebP sources */}
      <source
        type="image/webp"
        srcSet={`
          ${basePath}_small.webp 400w,
          ${basePath}_medium.webp 800w,
          ${basePath}_large.webp 1200w
        `}
        sizes={sizes || "(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"}
      />
      
      {/* Fallback JPEG */}
      <source
        type="image/jpeg"
        srcSet={`
          ${basePath}_small.jpg 400w,
          ${basePath}_medium.jpg 800w,
          ${basePath}_large.jpg 1200w
        `}
        sizes={sizes || "(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"}
      />
      
      {/* Ultimate fallback */}
      <Image src={src} alt={alt} loading="lazy" />
    </picture>
  );
};
```

### 1.3 Lazy Loading Images

```tsx
// Use Intersection Observer for lazy loading
import { useEffect, useRef, useState } from 'react';

export const useLazyLoad = () => {
  const [isVisible, setIsVisible] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.disconnect();
        }
      },
      { rootMargin: '50px' }
    );

    if (ref.current) {
      observer.observe(ref.current);
    }

    return () => observer.disconnect();
  }, []);

  return { ref, isVisible };
};

// Usage
const LazyImage = ({ src, alt }: { src: string; alt: string }) => {
  const { ref, isVisible } = useLazyLoad();
  
  return (
    <div ref={ref}>
      {isVisible ? (
        <img src={src} alt={alt} />
      ) : (
        <div style={{ height: 200, background: '#f0f0f0' }} />
      )}
    </div>
  );
};
```

### 1.4 Font Optimization

```html
<!-- frontend/public/index.html -->
<head>
  <!-- Preconnect to font CDN -->
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  
  <!-- Load fonts with display=swap -->
  <link 
    href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@600;700;800&display=swap" 
    rel="stylesheet" 
  />
</head>
```

Or use local fonts:

```css
/* frontend/src/fonts.css */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Bold.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}
```

### 1.5 Bundle Size Reduction

#### Analyze Bundle

```bash
cd frontend
npm run build
npx source-map-explorer 'build/static/js/*.js'
```

#### Tree Shaking

```javascript
// Import only what you need
// ❌ Bad
import _ from 'lodash';

// ✅ Good
import debounce from 'lodash/debounce';
```

#### Remove Unused Dependencies

```bash
npm install -g depcheck
depcheck
```

### 1.6 Caching Strategy

```typescript
// frontend/src/services/api.ts
import { QueryClient } from '@tanstack/react-query';

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5 minutes
      cacheTime: 10 * 60 * 1000, // 10 minutes
      refetchOnWindowFocus: false,
      retry: 1,
      // Add cache keys
      queryKeyHashFn: (queryKey) => JSON.stringify(queryKey),
    },
  },
});

// Prefetch critical data
queryClient.prefetchQuery(['settings'], fetchSettings);
```

---

## 2. Backend Performance

### 2.1 Database Query Optimization

#### Use Database Indexes

```sql
-- Add indexes for frequently queried columns
CREATE INDEX idx_articles_published ON articles(published, published_at DESC);
CREATE INDEX idx_articles_slug ON articles(slug);
CREATE INDEX idx_articles_category ON articles(category_id);
CREATE INDEX idx_players_team ON players(team_id);
CREATE INDEX idx_matches_date ON matches(match_date);

-- Composite indexes
CREATE INDEX idx_articles_published_featured ON articles(published, featured, published_at DESC);
```

#### N+1 Query Prevention

```go
// ❌ Bad - N+1 queries
var articles []models.Article
db.Find(&articles)
for _, article := range articles {
    db.Model(&article).Association("Category").Find(&article.Category)
}

// ✅ Good - Single query with joins
var articles []models.Article
db.Preload("Category").Preload("Author").Find(&articles)
```

#### Pagination

```go
func GetArticlesPaginated(c *gin.Context, db *gorm.DB) {
    page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
    pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
    
    if page < 1 {
        page = 1
    }
    if pageSize > 100 {
        pageSize = 100 // Prevent excessive queries
    }
    
    var articles []models.Article
    var total int64
    
    db.Model(&models.Article{}).Where("published = ?", true).Count(&total)
    
    offset := (page - 1) * pageSize
    db.Where("published = ?", true).
        Order("published_at DESC").
        Limit(pageSize).
        Offset(offset).
        Preload("Category").
        Find(&articles)
    
    c.JSON(http.StatusOK, gin.H{
        "items":      articles,
        "page":       page,
        "page_size":  pageSize,
        "total":      total,
        "total_pages": (total + int64(pageSize) - 1) / int64(pageSize),
    })
}
```

### 2.2 Caching Layer

#### Redis Integration

```go
// pkg/cache/redis.go
package cache

import (
    "context"
    "encoding/json"
    "time"
    
    "github.com/redis/go-redis/v9"
)

var RedisClient *redis.Client

func InitRedis(addr string) {
    RedisClient = redis.NewClient(&redis.Options{
        Addr: addr,
        DB:   0,
    })
}

func Get(ctx context.Context, key string, dest interface{}) error {
    val, err := RedisClient.Get(ctx, key).Result()
    if err != nil {
        return err
    }
    return json.Unmarshal([]byte(val), dest)
}

func Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
    json, err := json.Marshal(value)
    if err != nil {
        return err
    }
    return RedisClient.Set(ctx, key, json, expiration).Err()
}

// Usage in controller
func (bc *BaseController) GetArticles(c *gin.Context) {
    cacheKey := "articles:published"
    
    var articles []models.Article
    err := cache.Get(c, cacheKey, &articles)
    
    if err == nil {
        c.JSON(http.StatusOK, articles)
        return
    }
    
    // Cache miss - fetch from DB
    bc.DB.Where("published = ?", true).Find(&articles)
    cache.Set(c, cacheKey, articles, 5*time.Minute)
    
    c.JSON(http.StatusOK, articles)
}
```

#### In-Memory Cache

```go
// Simple in-memory cache for small datasets
type Cache struct {
    sync.RWMutex
    data map[string]cacheItem
}

type cacheItem struct {
    value      interface{}
    expiration time.Time
}

func (c *Cache) Get(key string) (interface{}, bool) {
    c.RLock()
    defer c.RUnlock()
    
    item, exists := c.data[key]
    if !exists || time.Now().After(item.expiration) {
        return nil, false
    }
    
    return item.value, true
}

func (c *Cache) Set(key string, value interface{}, ttl time.Duration) {
    c.Lock()
    defer c.Unlock()
    
    c.data[key] = cacheItem{
        value:      value,
        expiration: time.Now().Add(ttl),
    }
}
```

### 2.3 Connection Pooling

```go
// main.go or database initialization
sqlDB, _ := db.DB()

// Set connection pool parameters
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
sqlDB.SetConnMaxIdleTime(10 * time.Minute)
```

### 2.4 Response Compression

```go
// Use gzip middleware
import "github.com/gin-contrib/gzip"

func main() {
    r := gin.Default()
    r.Use(gzip.Gzip(gzip.DefaultCompression))
    // ...
}
```

### 2.5 HTTP Caching Headers

```go
func (bc *BaseController) GetPublicData(c *gin.Context) {
    // Set cache headers for public data
    c.Header("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400")
    c.Header("ETag", generateETag(data))
    
    // Check If-None-Match header
    if c.GetHeader("If-None-Match") == etag {
        c.Status(http.StatusNotModified)
        return
    }
    
    c.JSON(http.StatusOK, data)
}
```

---

## 3. Database Performance

### 3.1 Query Optimization

```sql
-- Use EXPLAIN ANALYZE to check query performance
EXPLAIN ANALYZE
SELECT * FROM articles 
WHERE published = true 
ORDER BY published_at DESC 
LIMIT 20;

-- Add missing indexes based on EXPLAIN output
```

### 3.2 Vacuum and Analyze

```sql
-- Regular maintenance
VACUUM ANALYZE articles;
VACUUM ANALYZE players;
VACUUM ANALYZE matches;

-- Schedule in cron
0 2 * * * psql -d fotbal_club -c "VACUUM ANALYZE"
```

### 3.3 Connection Pooling with PgBouncer

```ini
# pgbouncer.ini
[databases]
fotbal_club = host=localhost port=5432 dbname=fotbal_club

[pgbouncer]
listen_port = 6432
listen_addr = *
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
```

---

## 4. Monitoring & Profiling

### 4.1 Go Profiling

```go
import _ "net/http/pprof"

func main() {
    // Enable pprof in development
    if config.AppConfig.Debug {
        go func() {
            log.Println(http.ListenAndServe("localhost:6060", nil))
        }()
    }
    
    // ... rest of main
}
```

Access profiles:
- CPU: `http://localhost:6060/debug/pprof/profile?seconds=30`
- Memory: `http://localhost:6060/debug/pprof/heap`
- Goroutines: `http://localhost:6060/debug/pprof/goroutine`

Analyze:
```bash
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
```

### 4.2 Frontend Performance Monitoring

```typescript
// frontend/src/reportWebVitals.ts
import { Metric } from 'web-vitals';

const reportWebVitals = (onPerfEntry?: (metric: Metric) => void) => {
  if (onPerfEntry && onPerfEntry instanceof Function) {
    import('web-vitals').then(({ onCLS, onFID, onFCP, onLCP, onTTFB }) => {
      onCLS(onPerfEntry);
      onFID(onPerfEntry);
      onFCP(onPerfEntry);
      onLCP(onPerfEntry);
      onTTFB(onPerfEntry);
    });
  }
};

// Send to analytics
reportWebVitals((metric) => {
  console.log(metric);
  // Send to analytics endpoint
  fetch('/api/v1/analytics/vitals', {
    method: 'POST',
    body: JSON.stringify(metric),
  });
});
```

### 4.3 Database Query Logging

```go
// Enable query logging in development
if config.AppConfig.Debug {
    db = db.Debug()
}

// Or custom logger
newLogger := logger.New(
    log.New(os.Stdout, "\r\n", log.LstdFlags),
    logger.Config{
        SlowThreshold:             200 * time.Millisecond,
        LogLevel:                  logger.Warn,
        IgnoreRecordNotFoundError: true,
        Colorful:                  true,
    },
)

db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
    Logger: newLogger,
})
```

---

## 5. CDN & Static Assets

### 5.1 Use CDN for Static Assets

```nginx
# nginx.conf
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}
```

### 5.2 Asset Versioning

```javascript
// In build process - add hash to filenames
output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
}
```

---

## 6. Performance Budget

Set performance budgets to prevent regression:

```javascript
// budget.json
{
  "budgets": [
    {
      "resourceSizes": [
        {
          "resourceType": "script",
          "budget": 300
        },
        {
          "resourceType": "total",
          "budget": 500
        }
      ],
      "resourceCounts": [
        {
          "resourceType": "third-party",
          "budget": 10
        }
      ]
    }
  ]
}
```

---

## Performance Checklist

### Frontend
- [ ] Code splitting implemented
- [ ] Images optimized (WebP, responsive)
- [ ] Lazy loading for images/components
- [ ] Fonts optimized (display=swap, preconnect)
- [ ] Bundle size < 300KB (gzipped)
- [ ] No console.logs in production
- [ ] Service worker for caching
- [ ] Debounced search inputs

### Backend
- [ ] Database indexes on query columns
- [ ] Connection pooling configured
- [ ] Response compression enabled
- [ ] Cache headers set appropriately
- [ ] N+1 queries eliminated
- [ ] API pagination implemented
- [ ] Redis cache for hot data
- [ ] Slow query logging enabled

### Database
- [ ] Indexes created
- [ ] Regular VACUUM ANALYZE
- [ ] PgBouncer for connection pooling
- [ ] Query performance analyzed
- [ ] Backup strategy defined

### Monitoring
- [ ] Application metrics tracked
- [ ] Error tracking configured
- [ ] Performance alerts set
- [ ] Database metrics monitored
- [ ] CDN/cache hit rates tracked

---

## Expected Improvements

After implementing these optimizations:

- **Page Load Time**: 50-70% faster
- **Time to Interactive**: 60-80% faster
- **First Contentful Paint**: 40-60% faster
- **Bundle Size**: 50-70% smaller
- **Server Response Time**: 30-50% faster
- **Database Query Time**: 40-70% faster
- **Lighthouse Score**: 90+ on all metrics
