# MinIO Package Usage Examples

## Basic Setup

```go
package main

import (
    "context"
    "log"
    "path/to/pkg/minio"
)

// Implement the Logger interface
type SimpleLogger struct{}

func (l *SimpleLogger) Info(message string, fields map[string]interface{}) {
    log.Printf("[INFO] %s: %+v", message, fields)
}

func (l *SimpleLogger) Error(message string, fields map[string]interface{}) {
    log.Printf("[ERROR] %s: %+v", message, fields)
}

func (l *SimpleLogger) Debug(message string, fields map[string]interface{}) {
    log.Printf("[DEBUG] %s: %+v", message, fields)
}

func (l *SimpleLogger) Warn(message string, fields map[string]interface{}) {
    log.Printf("[WARN] %s: %+v", message, fields)
}

func main() {
    // Create configuration
    config := minio.Config{
        Endpoint:        "localhost:9000",
        AccessKeyID:     "minioadmin",
        SecretAccessKey: "minioadmin",
        UseSSL:          false,
        DefaultBucket:   "files",
        HierarchicalBucket: "documents",
    }

    // Create logger
    logger := &SimpleLogger{}

    // Create connection manager
    connManager, err := minio.NewConnectionManager(config, logger)
    if err != nil {
        log.Fatal("Failed to create connection manager:", err)
    }

    // Connect to MinIO
    if err := connManager.Connect(); err != nil {
        log.Fatal("Failed to connect to MinIO:", err)
    }
    defer connManager.Disconnect(context.Background())

    // Create service
    service := minio.NewService(connManager, config, logger)

    // Use the service...
}
```

## Normal File Storage

### Upload an Image

```go
func uploadImage(service *minio.Service) error {
    // Open image file
    file, err := os.Open("profile.jpg")
    if err != nil {
        return err
    }
    defer file.Close()

    // Upload to normal storage
    return service.Upload(context.Background(), "files", "images/profile.jpg", file, minio.UploadOptions{
        ContentType: "image/jpeg",
        Metadata: map[string]string{
            "user_id": "12345",
            "type":    "profile_image",
        },
    })
}
```

### Download and Serve a File

```go
func serveFile(service *minio.Service, w http.ResponseWriter, bucket, objectName string) error {
    // Download file
    reader, err := service.Download(context.Background(), bucket, objectName, minio.DownloadOptions{})
    if err != nil {
        return err
    }
    defer reader.Close()

    // Get file info for headers
    info, err := service.GetInfo(context.Background(), bucket, objectName)
    if err != nil {
        return err
    }

    // Set response headers
    w.Header().Set("Content-Type", info.ContentType)
    w.Header().Set("Content-Length", fmt.Sprintf("%d", info.Size))

    // Copy file content to response
    _, err = io.Copy(w, reader)
    return err
}
```

### Upload with Stream Processing

```go
func uploadWithProgress(service *minio.Service, file io.Reader, filename string) error {
    // Create a progress reader
    progressReader := &ProgressReader{
        Reader:   file,
        Total:    0, // Set total size if known
        Reporter: func(bytesRead int64) {
            log.Printf("Uploaded %d bytes", bytesRead)
        },
    }

    return service.Upload(context.Background(), "files", "uploads/"+filename, progressReader, minio.UploadOptions{
        ContentType: mime.TypeByExtension(filepath.Ext(filename)),
    })
}

type ProgressReader struct {
    Reader   io.Reader
    Total    int64
    Reporter func(bytesRead int64)
}

func (pr *ProgressReader) Read(p []byte) (int, error) {
    n, err := pr.Reader.Read(p)
    if n > 0 && pr.Reporter != nil {
        pr.Total += int64(n)
        pr.Reporter(pr.Total)
    }
    return n, err
}
```

## Hierarchical Document Storage

### Upload a Contract

```go
func uploadContract(service *minio.Service) error {
    // Open contract file
    file, err := os.Open("contract.pdf")
    if err != nil {
        return err
    }
    defer file.Close()

    // Upload to hierarchical storage
    docPath, err := service.UploadDocument(context.Background(), file, "service_contract.pdf", minio.HierarchicalUploadOptions{
        UploadOptions: minio.UploadOptions{
            ContentType: "application/pdf",
            Metadata: map[string]string{
                "contract_id": "CTR-2024-001",
                "client_id":   "CLIENT-123",
                "value":       "50000.00",
                "signed":      "true",
                "department":  "legal",
            },
        },
        Category:    "contracts",
        SubCategory: "2024",
        Tags:        []string{"legal", "service", "active"},
    })

    if err != nil {
        return err
    }

    log.Printf("Contract uploaded to: %s", docPath)
    // Output: contracts/2024/legal_service_active/1640995200/service_contract.pdf

    return nil
}
```

### List Documents by Category

```go
func listContractsByYear(service *minio.Service, year string) error {
    result, err := service.ListDocuments(context.Background(), "contracts", year, minio.ListOptions{
        MaxKeys: 50,
    })
    if err != nil {
        return err
    }

    log.Printf("Found %d documents in contracts/%s", len(result.Objects), year)

    for _, doc := range result.Objects {
        log.Printf("- %s (%d bytes)", doc.Name, doc.Size)
    }

    return nil
}
```

### Search Documents

```go
func searchLegalDocuments(service *minio.Service) error {
    result, err := service.SearchDocuments(context.Background(), "legal", "contracts", "2024", minio.ListOptions{
        MaxKeys: 20,
    })
    if err != nil {
        return err
    }

    log.Printf("Found %d legal documents", len(result.Objects))

    return nil
}
```

## File Operations

### Copy Files Between Buckets

```go
func backupFile(service *minio.Service, srcBucket, srcObject, destBucket, destObject string) error {
    return service.Copy(context.Background(), srcBucket, srcObject, destBucket, destObject)
}
```

### Generate Presigned URLs

```go
func generateDownloadLink(service *minio.Service, bucket, objectName string) (string, error) {
    // Generate URL valid for 1 hour
    return service.GetPresignedURL(context.Background(), bucket, objectName, 3600)
}
```

### Check File Existence

```go
func fileExists(service *minio.Service, bucket, objectName string) bool {
    exists, err := service.Exists(context.Background(), bucket, objectName)
    if err != nil {
        log.Printf("Error checking file existence: %v", err)
        return false
    }
    return exists
}
```

## Bucket Management

### Create Required Buckets

```go
func ensureBucketsExist(connManager *minio.ConnectionManager) error {
    ctx := context.Background()

    // Create default bucket
    if err := connManager.CreateBucket(ctx, "files"); err != nil {
        return fmt.Errorf("failed to create files bucket: %w", err)
    }

    // Create hierarchical bucket
    if err := connManager.CreateBucket(ctx, "documents"); err != nil {
        return fmt.Errorf("failed to create documents bucket: %w", err)
    }

    return nil
}
```

### List All Buckets

```go
func listAllBuckets(connManager *minio.ConnectionManager) error {
    buckets, err := connManager.ListBuckets(context.Background())
    if err != nil {
        return err
    }

    log.Printf("Available buckets:")
    for _, bucket := range buckets {
        log.Printf("- %s", bucket)
    }

    return nil
}
```

## Error Handling

```go
func handleUploadError(err error) {
    switch {
    case errors.Is(err, minio.ErrInvalidConfig):
        log.Println("Configuration error:", err)
    case errors.Is(err, minio.ErrNotConnected):
        log.Println("Connection error:", err)
    case errors.Is(err, minio.ErrUploadFailed):
        log.Println("Upload failed:", err)
    case errors.Is(err, minio.ErrFileTooLarge):
        log.Println("File too large:", err)
    case errors.Is(err, minio.ErrInvalidFileType):
        log.Println("Invalid file type:", err)
    default:
        log.Println("Unknown error:", err)
    }
}
```

## Integration with HTTP Handlers

### File Upload Handler

```go
func uploadHandler(service *minio.Service) gin.HandlerFunc {
    return func(c *gin.Context) {
        // Get uploaded file
        file, err := c.FormFile("file")
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
            return
        }

        // Open uploaded file
        src, err := file.Open()
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
            return
        }
        defer src.Close()

        // Generate unique filename
        filename := fmt.Sprintf("%d_%s", time.Now().Unix(), file.Filename)
        objectName := fmt.Sprintf("uploads/%s", filename)

        // Upload file
        err = service.Upload(c.Request.Context(), "files", objectName, src, minio.UploadOptions{
            ContentType: file.Header.Get("Content-Type"),
            Metadata: map[string]string{
                "original_name": file.Filename,
                "uploaded_by": "user_id", // Get from context
            },
        })

        if err != nil {
            handleUploadError(err)
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Upload failed"})
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "message": "File uploaded successfully",
            "filename": filename,
            "path": objectName,
        })
    }
}
```

### Document Upload Handler

```go
func uploadDocumentHandler(service *minio.Service) gin.HandlerFunc {
    return func(c *gin.Context) {
        // Get form data
        category := c.PostForm("category")
        subCategory := c.PostForm("subcategory")
        tags := c.PostFormArray("tags[]")

        // Get uploaded file
        file, err := c.FormFile("document")
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": "No document provided"})
            return
        }

        // Open document
        src, err := file.Open()
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open document"})
            return
        }
        defer src.Close()

        // Upload to hierarchical storage
        docPath, err := service.UploadDocument(c.Request.Context(), src, file.Filename, minio.HierarchicalUploadOptions{
            UploadOptions: minio.UploadOptions{
                ContentType: file.Header.Get("Content-Type"),
                Metadata: map[string]string{
                    "uploaded_by": "user_id", // Get from context
                    "department":  c.PostForm("department"),
                },
            },
            Category:    category,
            SubCategory: subCategory,
            Tags:        tags,
        })

        if err != nil {
            handleUploadError(err)
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Document upload failed"})
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "message": "Document uploaded successfully",
            "path": docPath,
        })
    }
}
```

## Configuration Examples

### Development Configuration

```go
config := minio.Config{
    Endpoint:        "localhost:9000",
    AccessKeyID:     "minioadmin",
    SecretAccessKey: "minioadmin",
    UseSSL:          false,
    Region:          "us-east-1",
    DefaultBucket:   "files",
    HierarchicalBucket: "documents",
    MaxFileSize:     50 * 1024 * 1024, // 50MB
    AllowedTypes:    []string{"image/", "application/pdf", "text/"},
}
```

### Production Configuration

```go
config := minio.Config{
    Endpoint:        "minio.example.com:443",
    AccessKeyID:     os.Getenv("MINIO_ACCESS_KEY"),
    SecretAccessKey: os.Getenv("MINIO_SECRET_KEY"),
    UseSSL:          true,
    Region:          "us-west-2",
    DefaultBucket:   "production-files",
    HierarchicalBucket: "production-documents",
    MaxFileSize:     100 * 1024 * 1024, // 100MB
    AllowedTypes:    []string{"image/jpeg", "image/png", "application/pdf"},
    ConnectTimeout:  30 * time.Second,
    RequestTimeout:  60 * time.Second,
}
```
