# Notification SDK

A comprehensive Go SDK for interacting with the Tender Management notification service. This SDK provides a clean, type-safe interface for sending various types of notifications including emails, SMS, push notifications, and OTP messages.

## Features

- **Multiple Notification Types**: Support for EMAIL, SMS, PUSH, and OTP notifications
- **Automatic Retries**: Configurable retry mechanism with exponential backoff
- **Structured Logging**: Built-in logging with structured fields
- **Fluent API**: Builder pattern for constructing complex notifications
- **Batch Operations**: Send multiple notifications efficiently
- **Type Safety**: Full type safety with comprehensive validation
- **Error Handling**: Detailed error types and proper error wrapping
- **Configuration**: Flexible configuration via environment variables or code
- **Health Checks**: Built-in health checking capabilities

## Installation

```bash
go get your-module/pkg/notification
```

## Quick Start

### Basic Usage

```go
package main

import (
    "context"
    "log"
    "your-module/pkg/notification"
    "your-module/pkg/logger" // Your logger implementation
)

func main() {
    // Create configuration
    config := notification.DefaultConfig()
    config.BaseURL = "http://127.0.0.1:9095" // Your notification service URL
    
    // Create logger (implement notification.Logger interface)
    logger := logger.New() // Your logger implementation
    
    // Create SDK
    sdk, err := notification.NewSDK(config, logger)
    if err != nil {
        log.Fatal("Failed to create notification SDK:", err)
    }
    
    ctx := context.Background()
    
    // Send a simple email
    err = sdk.QuickEmail(ctx, "user@example.com", "Hello World!")
    if err != nil {
        log.Printf("Failed to send email: %v", err)
    }
}
```

### Environment Configuration

Set environment variables for automatic configuration:

```bash
export NOTIFICATION_BASE_URL="http://127.0.0.1:9095"
export NOTIFICATION_TIMEOUT="30s"
export NOTIFICATION_RETRY_ATTEMPTS="3"
export NOTIFICATION_RETRY_DELAY="2s"
export NOTIFICATION_ENABLE_LOGGING="true"
export NOTIFICATION_USER_AGENT="MyApp-NotificationSDK/1.0"
```

Then load configuration:

```go
import "your-module/pkg/config"

// Load configuration from environment
cfg, err := config.LoadConfig(".", &notification.Config{})
if err != nil {
    log.Fatal("Failed to load config:", err)
}

sdk, err := notification.NewSDK(cfg, logger)
```

## API Reference

### Configuration

```go
type Config struct {
    BaseURL        string        `env:"NOTIFICATION_BASE_URL"`
    Timeout        time.Duration `env:"NOTIFICATION_TIMEOUT"`
    RetryAttempts  int           `env:"NOTIFICATION_RETRY_ATTEMPTS"`
    RetryDelay     time.Duration `env:"NOTIFICATION_RETRY_DELAY"`
    EnableLogging  bool          `env:"NOTIFICATION_ENABLE_LOGGING"`
    UserAgent      string        `env:"NOTIFICATION_USER_AGENT"`
}
```

### Creating SDK Instance

```go
// With default configuration
sdk, err := notification.NewSDK(nil, logger)

// With custom configuration
config := &notification.Config{
    BaseURL:       "http://localhost:9095",
    Timeout:       30 * time.Second,
    RetryAttempts: 3,
    RetryDelay:    2 * time.Second,
    EnableLogging: true,
    UserAgent:     "MyApp/1.0",
}
sdk, err := notification.NewSDK(config, logger)

// With custom HTTP client
httpClient := &http.Client{Timeout: 10 * time.Second}
sdk, err := notification.NewSDKWithClient(config, httpClient, logger)
```

### Sending Notifications

#### Simple Notifications

```go
ctx := context.Background()

// Email
response, err := sdk.SendEmail(ctx, "user@example.com", "Welcome!", "user123")

// SMS
response, err := sdk.SendSMS(ctx, "+1234567890", "Your code: 1234", "user123")

// Push Notification
response, err := sdk.SendPush(ctx, "device_token_here", "New message!", "user123")

// OTP
response, err := sdk.SendOTP(ctx, "+1234567890", "Your OTP: 5678", "user123")
```

#### Quick Notifications (without user ID)

```go
// Quick methods for simple use cases
err := sdk.QuickEmail(ctx, "user@example.com", "Hello!")
err := sdk.QuickSMS(ctx, "+1234567890", "Hello!")
err := sdk.QuickPush(ctx, "device_token", "Hello!")
err := sdk.QuickOTP(ctx, "+1234567890", "Code: 1234")
```

#### Custom Notifications

```go
request := &notification.NotificationRequest{
    EventType: notification.EventTypeEmail,
    Message:   "Custom notification message",
    Methods: notification.NotificationMethods{
        Email: "user@example.com",
        SMS:   "+1234567890", // Optional: multiple delivery methods
    },
    UserID: "user123",
}

response, err := sdk.SendNotification(ctx, request)
```

### Fluent Builder API

```go
// Build and send notification using fluent API
response, err := sdk.NewBuilder().
    SetEventType(notification.EventTypeEmail).
    SetMessage("Welcome to our service!").
    SetEmail("user@example.com").
    SetUserID("user123").
    Send(ctx)

// Build without sending
request, err := sdk.NewBuilder().
    SetEventType(notification.EventTypeSMS).
    SetMessage("Your verification code: 1234").
    SetSMS("+1234567890").
    SetUserID("user123").
    Build()

if err == nil {
    response, err := sdk.SendNotification(ctx, request)
}
```

### Batch Operations

```go
// Prepare multiple requests
requests := []*notification.NotificationRequest{
    {
        EventType: notification.EventTypeEmail,
        Message:   "Welcome email",
        Methods:   notification.NotificationMethods{Email: "user1@example.com"},
        UserID:    "user1",
    },
    {
        EventType: notification.EventTypeSMS,
        Message:   "Welcome SMS",
        Methods:   notification.NotificationMethods{SMS: "+1234567890"},
        UserID:    "user2",
    },
}

// Send batch
batchResponse, err := sdk.SendBatch(ctx, requests)
if err != nil {
    log.Printf("Batch failed: %v", err)
} else {
    log.Printf("Batch completed: %d success, %d failed", 
        batchResponse.Success, batchResponse.Failed)
    
    // Check individual results
    for _, resp := range batchResponse.Responses {
        if !resp.Success {
            log.Printf("Request %d failed: %s", resp.Index, resp.Error)
        }
    }
}
```

### Health Checks

```go
// Check if notification service is available
err := sdk.Health(ctx)
if err != nil {
    log.Printf("Notification service is unhealthy: %v", err)
} else {
    log.Println("Notification service is healthy")
}
```

## Event Types

The SDK supports the following notification types:

- `notification.EventTypeEmail` - Email notifications
- `notification.EventTypeSMS` - SMS notifications  
- `notification.EventTypePush` - Push notifications
- `notification.EventTypeOTP` - OTP notifications

## Error Handling

The SDK provides detailed error types for different scenarios:

```go
response, err := sdk.SendEmail(ctx, "invalid-email", "Hello!", "user123")
if err != nil {
    switch e := err.(type) {
    case notification.ErrHTTP:
        log.Printf("HTTP error %d: %s", e.StatusCode, e.Message)
    case notification.ErrValidation:
        log.Printf("Validation error in field %s: %s", e.Field, e.Message)
    case notification.ErrRetryExhausted:
        log.Printf("Retry exhausted after %d attempts: %v", e.Attempts, e.LastError)
    case notification.ErrInvalidConfig:
        log.Printf("Configuration error in field %s: %s", e.Field, e.Message)
    default:
        log.Printf("Unknown error: %v", err)
    }
}
```

## Logging

The SDK requires a logger that implements the `notification.Logger` interface:

```go
type Logger interface {
    Info(msg string, fields map[string]interface{})
    Error(msg string, fields map[string]interface{})
    Debug(msg string, fields map[string]interface{})
    Warn(msg string, fields map[string]interface{})
}
```

Example logger implementation with logrus:

```go
import "github.com/sirupsen/logrus"

type LogrusLogger struct {
    logger *logrus.Logger
}

func (l *LogrusLogger) Info(msg string, fields map[string]interface{}) {
    l.logger.WithFields(fields).Info(msg)
}

func (l *LogrusLogger) Error(msg string, fields map[string]interface{}) {
    l.logger.WithFields(fields).Error(msg)
}

func (l *LogrusLogger) Debug(msg string, fields map[string]interface{}) {
    l.logger.WithFields(fields).Debug(msg)
}

func (l *LogrusLogger) Warn(msg string, fields map[string]interface{}) {
    l.logger.WithFields(fields).Warn(msg)
}
```

## Testing

The SDK is designed to be easily testable. You can mock the HTTP client:

```go
import (
    "testing"
    "net/http"
    "your-module/pkg/notification"
)

// Mock HTTP client
type MockHTTPClient struct {
    DoFunc func(req *http.Request) (*http.Response, error)
}

func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
    return m.DoFunc(req)
}

// Mock logger
type MockLogger struct{}
func (l *MockLogger) Info(msg string, fields map[string]interface{}) {}
func (l *MockLogger) Error(msg string, fields map[string]interface{}) {}
func (l *MockLogger) Debug(msg string, fields map[string]interface{}) {}
func (l *MockLogger) Warn(msg string, fields map[string]interface{}) {}

func TestSendEmail(t *testing.T) {
    mockClient := &MockHTTPClient{
        DoFunc: func(req *http.Request) (*http.Response, error) {
            // Return mock response
            return &http.Response{
                StatusCode: 200,
                Body: io.NopCloser(strings.NewReader(`{"status":"Notification sent"}`)),
            }, nil
        },
    }
    
    config := notification.DefaultConfig()
    logger := &MockLogger{}
    
    sdk, err := notification.NewSDKWithClient(config, mockClient, logger)
    if err != nil {
        t.Fatal(err)
    }
    
    response, err := sdk.SendEmail(context.Background(), "test@example.com", "Test", "user123")
    if err != nil {
        t.Fatal(err)
    }
    
    if response.Status != "Notification sent" {
        t.Errorf("Expected 'Notification sent', got %s", response.Status)
    }
}
```

## Best Practices

1. **Configuration**: Use environment variables for configuration in production
2. **Context**: Always pass context for timeout and cancellation support
3. **Error Handling**: Check specific error types for appropriate handling
4. **Logging**: Implement structured logging for better observability
5. **Retries**: Configure appropriate retry settings based on your use case
6. **Validation**: Let the SDK handle validation, but provide clean input data
7. **Batch Operations**: Use batch operations for sending multiple notifications efficiently

## Examples

See the `examples/` directory for complete working examples:

- `basic_usage.go` - Basic notification sending
- `batch_operations.go` - Batch notification processing
- `fluent_api.go` - Using the builder pattern
- `error_handling.go` - Comprehensive error handling
- `configuration.go` - Different configuration methods

## Contributing

1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Ensure all tests pass
5. Submit a pull request

## License

This SDK is part of the Tender Management system and follows the same license terms. 