# Admin Models Integration Guide

## What We've Added

You now have:

1. **Database Models** (`models/database/admin.py`):
   - `AdminAuditLog` - Track all admin actions
   - `AdminNotification` - System alerts for admins
   - `AdminSetting` - Platform-wide settings
   - `PlatformAlert` - System health alerts
   - `AdminSession` - Track admin login sessions

2. **Pydantic Schemas** (`models/schemas/admin.py`):
   - Request/response models for all admin operations
   - Validation for bulk operations
   - System health check schemas

3. **Services**:
   - `AdminAuditService` - Audit logging operations
   - `AdminSettingsService` - Platform settings management

4. **API Endpoints**:
   - `/api/v1/admin/audit` - Audit log endpoints
   - `/api/v1/admin/settings` - Settings management
   - `/api/v1/admin/notifications` - Notifications & alerts (stubs)

---

## Step-by-Step Integration

### Step 1: Update Database

Add the new models to your database imports:

```python
# models/database/__init__.py
from .admin import (
    AdminAuditLog,
    AdminNotification,
    AdminSetting,
    PlatformAlert,
    AdminSession
)
```

Run database migration:
```bash
# Create migration
alembic revision --autogenerate -m "Add admin models"

# Apply migration
alembic upgrade head
```

### Step 2: Update Admin API Router

```python
# app/api/v1/admin/__init__.py
from fastapi import APIRouter
from . import auth, stores, users, dashboard, marketplace, audit, settings, notifications

router = APIRouter(prefix="/admin", tags=["admin"])

# Include all admin routers
router.include_router(auth.router)
router.include_router(stores.router)
router.include_router(users.router)
router.include_router(dashboard.router)
router.include_router(marketplace.router)
router.include_router(audit.router)  # NEW
router.include_router(settings.router)  # NEW
router.include_router(notifications.router)  # NEW
```

### Step 3: Add Audit Logging to Existing Admin Operations

Update your `admin_service.py` to log actions:

```python
# app/services/admin_service.py
from app.services.admin_audit_service import admin_audit_service

class AdminService:

    def create_store_with_owner(
        self, db: Session, store_data: StoreCreate
    ) -> Tuple[Store, User, str]:
        """Create store with owner user account."""

        # ... existing code ...

        store, owner_user, temp_password = # ... your creation logic

        # LOG THE ACTION
        admin_audit_service.log_action(
            db=db,
            admin_user_id=current_admin_id,  # You'll need to pass this
            action="create_store",
            target_type="store",
            target_id=str(store.id),
            details={
                "store_code": store.store_code,
                "subdomain": store.subdomain,
                "owner_email": owner_user.email
            }
        )

        return store, owner_user, temp_password

    def toggle_store_status(
        self, db: Session, store_id: int, admin_user_id: int
    ) -> Tuple[Store, str]:
        """Toggle store status with audit logging."""

        store = self._get_store_by_id_or_raise(db, store_id)
        old_status = store.is_active

        # ... toggle logic ...

        # LOG THE ACTION
        admin_audit_service.log_action(
            db=db,
            admin_user_id=admin_user_id,
            action="toggle_store_status",
            target_type="store",
            target_id=str(store_id),
            details={
                "old_status": "active" if old_status else "inactive",
                "new_status": "active" if store.is_active else "inactive"
            }
        )

        return store, message
```

### Step 4: Update API Endpoints to Pass Admin User ID

Your API endpoints need to pass the current admin's ID to service methods:

```python
# app/api/v1/admin/stores.py

@router.post("", response_model=StoreResponse)
def create_store_with_owner(
    store_data: StoreCreate,
    db: Session = Depends(get_db),
    current_admin: User = Depends(get_current_admin_api),
):
    """Create store with audit logging."""

    store, owner_user, temp_password = admin_service.create_store_with_owner(
        db=db,
        store_data=store_data,
        admin_user_id=current_admin.id  # Pass admin ID for audit logging
    )

    # Audit log is automatically created inside the service

    return {
        **StoreResponse.model_validate(store).model_dump(),
        "owner_email": owner_user.email,
        "owner_username": owner_user.username,
        "temporary_password": temp_password,
        "login_url": f"{store.subdomain}.platform.com/store/login"
    }


@router.put("/{store_id}/status")
def toggle_store_status(
    store_id: int,
    db: Session = Depends(get_db),
    current_admin: User = Depends(get_current_admin_api),
):
    """Toggle store status with audit logging."""
    store, message = admin_service.toggle_store_status(
        db=db,
        store_id=store_id,
        admin_user_id=current_admin.id  # Pass for audit
    )
    return {"message": message, "store": StoreResponse.model_validate(store)}
```

### Step 5: Add Request Context to Audit Logs

To capture IP address and user agent, use FastAPI's Request object:

```python
# app/api/v1/admin/stores.py
from fastapi import Request

@router.delete("/{store_id}")
def delete_store(
    store_id: int,
    request: Request,  # Add request parameter
    confirm: bool = Query(False),
    db: Session = Depends(get_db),
    current_admin: User = Depends(get_current_admin_api),
):
    """Delete store with full audit trail."""

    if not confirm:
        raise HTTPException(status_code=400, detail="Confirmation required")

    # Get request metadata
    ip_address = request.client.host if request.client else None
    user_agent = request.headers.get("user-agent")

    message = admin_service.delete_store(db, store_id)

    # Log with full context
    admin_audit_service.log_action(
        db=db,
        admin_user_id=current_admin.id,
        action="delete_store",
        target_type="store",
        target_id=str(store_id),
        ip_address=ip_address,
        user_agent=user_agent,
        details={"confirm": True}
    )

    return {"message": message}
```

---

## Example: Platform Settings Usage

### Creating Default Settings

```python
# scripts/init_platform_settings.py
from app.core.database import SessionLocal
from app.services.admin_settings_service import admin_settings_service
from models.schemas.admin import AdminSettingCreate

db = SessionLocal()

# Create default platform settings
settings = [
    AdminSettingCreate(
        key="max_stores_allowed",
        value="1000",
        value_type="integer",
        category="system",
        description="Maximum number of stores allowed on the platform",
        is_public=False
    ),
    AdminSettingCreate(
        key="maintenance_mode",
        value="false",
        value_type="boolean",
        category="system",
        description="Enable maintenance mode (blocks all non-admin access)",
        is_public=True
    ),
    AdminSettingCreate(
        key="store_trial_days",
        value="30",
        value_type="integer",
        category="system",
        description="Default trial period for new stores (days)",
        is_public=False
    ),
    AdminSettingCreate(
        key="stripe_publishable_key",
        value="pk_test_...",
        value_type="string",
        category="payments",
        description="Stripe publishable key",
        is_public=True
    ),
    AdminSettingCreate(
        key="stripe_secret_key",
        value="sk_test_...",
        value_type="string",
        category="payments",
        description="Stripe secret key",
        is_encrypted=True,
        is_public=False
    )
]

for setting_data in settings:
    try:
        admin_settings_service.upsert_setting(db, setting_data, admin_user_id=1)
        print(f"✓ Created setting: {setting_data.key}")
    except Exception as e:
        print(f"✗ Failed to create {setting_data.key}: {e}")

db.close()
```

### Using Settings in Your Code

```python
# app/services/store_service.py
from app.services.admin_settings_service import admin_settings_service

def can_create_store(db: Session) -> bool:
    """Check if platform allows creating more stores."""

    max_stores = admin_settings_service.get_setting_value(
        db=db,
        key="max_stores_allowed",
        default=1000
    )

    current_count = db.query(Store).count()

    return current_count < max_stores


def is_maintenance_mode(db: Session) -> bool:
    """Check if platform is in maintenance mode."""
    return admin_settings_service.get_setting_value(
        db=db,
        key="maintenance_mode",
        default=False
    )
```

---

## Frontend Integration

### Admin Dashboard with Audit Logs

```html
<!-- templates/admin/audit_logs.html -->
<div x-data="auditLogs()" x-init="loadLogs()">
    <h1>Audit Logs</h1>

    <!-- Filters -->
    <div class="filters">
        <select x-model="filters.action" @change="loadLogs()">
            <option value="">All Actions</option>
            <option value="create_store">Create Store</option>
            <option value="delete_store">Delete Store</option>
            <option value="toggle_store_status">Toggle Status</option>
            <option value="update_setting">Update Setting</option>
        </select>

        <select x-model="filters.target_type" @change="loadLogs()">
            <option value="">All Targets</option>
            <option value="store">Stores</option>
            <option value="user">Users</option>
            <option value="setting">Settings</option>
        </select>
    </div>

    <!-- Logs Table -->
    <table class="data-table">
        <thead>
            <tr>
                <th>Timestamp</th>
                <th>Admin</th>
                <th>Action</th>
                <th>Target</th>
                <th>Details</th>
                <th>IP Address</th>
            </tr>
        </thead>
        <tbody>
            <template x-for="log in logs" :key="log.id">
                <tr>
                    <td x-text="formatDate(log.created_at)"></td>
                    <td x-text="log.admin_username"></td>
                    <td>
                        <span class="badge" x-text="log.action"></span>
                    </td>
                    <td x-text="`${log.target_type}:${log.target_id}`"></td>
                    <td>
                        <button @click="showDetails(log)">View</button>
                    </td>
                    <td x-text="log.ip_address"></td>
                </tr>
            </template>
        </tbody>
    </table>

    <!-- Pagination -->
    <div class="pagination">
        <button @click="previousPage()" :disabled="skip === 0">Previous</button>
        <span x-text="`Page ${currentPage} of ${totalPages}`"></span>
        <button @click="nextPage()" :disabled="!hasMore">Next</button>
    </div>
</div>

<script>
function auditLogs() {
    return {
        logs: [],
        filters: {
            action: '',
            target_type: '',
            admin_user_id: null
        },
        skip: 0,
        limit: 50,
        total: 0,

        async loadLogs() {
            const params = new URLSearchParams({
                skip: this.skip,
                limit: this.limit,
                ...this.filters
            });

            const response = await apiClient.get(`/api/v1/admin/audit/logs?${params}`);
            this.logs = response.logs;
            this.total = response.total;
        },

        showDetails(log) {
            // Show modal with full details
            console.log('Details:', log.details);
        },

        formatDate(date) {
            return new Date(date).toLocaleString();
        },

        get currentPage() {
            return Math.floor(this.skip / this.limit) + 1;
        },

        get totalPages() {
            return Math.ceil(this.total / this.limit);
        },

        get hasMore() {
            return this.skip + this.limit < this.total;
        },

        nextPage() {
            this.skip += this.limit;
            this.loadLogs();
        },

        previousPage() {
            this.skip = Math.max(0, this.skip - this.limit);
            this.loadLogs();
        }
    }
}
</script>
```

### Platform Settings Management

```html
<!-- templates/admin/settings.html -->
<div x-data="platformSettings()" x-init="loadSettings()">
    <h1>Platform Settings</h1>

    <!-- Category Tabs -->
    <div class="tabs">
        <button
            @click="selectedCategory = 'system'"
            :class="{'active': selectedCategory === 'system'}"
        >System</button>
        <button
            @click="selectedCategory = 'security'"
            :class="{'active': selectedCategory === 'security'}"
        >Security</button>
        <button
            @click="selectedCategory = 'payments'"
            :class="{'active': selectedCategory === 'payments'}"
        >Payments</button>
    </div>

    <!-- Settings List -->
    <div class="settings-list">
        <template x-for="setting in filteredSettings" :key="setting.id">
            <div class="setting-item">
                <div class="setting-header">
                    <h3 x-text="setting.key"></h3>
                    <span class="badge" x-text="setting.value_type"></span>
                </div>
                <p class="setting-description" x-text="setting.description"></p>

                <div class="setting-value">
                    <input
                        type="text"
                        :value="setting.value"
                        @change="updateSetting(setting.key, $event.target.value)"
                    >
                    <span class="updated-at" x-text="`Updated: ${formatDate(setting.updated_at)}`"></span>
                </div>
            </div>
        </template>
    </div>

    <!-- Add New Setting -->
    <button @click="showAddModal = true" class="btn-primary">
        Add New Setting
    </button>
</div>

<script>
function platformSettings() {
    return {
        settings: [],
        selectedCategory: 'system',
        showAddModal: false,

        async loadSettings() {
            const response = await apiClient.get('/api/v1/admin/settings');
            this.settings = response.settings;
        },

        get filteredSettings() {
            if (!this.selectedCategory) return this.settings;
            return this.settings.filter(s => s.category === this.selectedCategory);
        },

        async updateSetting(key, newValue) {
            try {
                await apiClient.put(`/api/v1/admin/settings/${key}`, {
                    value: newValue
                });
                showNotification('Setting updated successfully', 'success');
                this.loadSettings();
            } catch (error) {
                showNotification('Failed to update setting', 'error');
            }
        },

        formatDate(date) {
            return new Date(date).toLocaleString();
        }
    }
}
</script>
```

---

## Testing the New Features

### Test Audit Logging

```python
# tests/test_admin_audit.py
import pytest
from app.services.admin_audit_service import admin_audit_service

def test_log_admin_action(db_session, test_admin_user):
    """Test logging admin actions."""
    log = admin_audit_service.log_action(
        db=db_session,
        admin_user_id=test_admin_user.id,
        action="create_store",
        target_type="store",
        target_id="123",
        details={"store_code": "TEST"}
    )

    assert log is not None
    assert log.action == "create_store"
    assert log.target_type == "store"
    assert log.details["store_code"] == "TEST"

def test_query_audit_logs(db_session, test_admin_user):
    """Test querying audit logs with filters."""
    # Create test logs
    for i in range(5):
        admin_audit_service.log_action(
            db=db_session,
            admin_user_id=test_admin_user.id,
            action=f"test_action_{i}",
            target_type="test",
            target_id=str(i)
        )

    # Query logs
    from models.schemas.admin import AdminAuditLogFilters
    filters = AdminAuditLogFilters(limit=10)
    logs = admin_audit_service.get_audit_logs(db_session, filters)

    assert len(logs) == 5
```

### Test Platform Settings

```python
# tests/test_admin_settings.py
def test_create_setting(db_session, test_admin_user):
    """Test creating platform setting."""
    from models.schemas.admin import AdminSettingCreate

    setting_data = AdminSettingCreate(
        key="test_setting",
        value="test_value",
        value_type="string",
        category="test"
    )

    result = admin_settings_service.create_setting(
        db=db_session,
        setting_data=setting_data,
        admin_user_id=test_admin_user.id
    )

    assert result.key == "test_setting"
    assert result.value == "test_value"

def test_get_setting_value_with_type_conversion(db_session):
    """Test getting setting values with proper type conversion."""
    # Create integer setting
    setting_data = AdminSettingCreate(
        key="max_stores",
        value="100",
        value_type="integer",
        category="system"
    )
    admin_settings_service.create_setting(db_session, setting_data, 1)

    # Get value (should be converted to int)
    value = admin_settings_service.get_setting_value(db_session, "max_stores")
    assert isinstance(value, int)
    assert value == 100
```

---

## Summary

You now have a complete admin infrastructure with:

✅ **Audit Logging**: Track all admin actions for compliance
✅ **Platform Settings**: Manage global configuration
✅ **Notifications**: System alerts for admins (structure ready)
✅ **Platform Alerts**: Health monitoring (structure ready)
✅ **Session Tracking**: Monitor admin logins (structure ready)

### Next Steps

1. **Apply database migrations** to create new tables
2. **Update admin API router** to include new endpoints
3. **Add audit logging** to existing admin operations
4. **Create default platform settings** using the script
5. **Build frontend pages** for audit logs and settings
6. **Implement notification service** (notifications.py stubs)
7. **Add monitoring** for platform alerts

These additions make your platform production-ready with full compliance and monitoring capabilities!
