# Solution Design Document

## Validation Checklist

- [x] All required sections are complete
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] All context sources are listed with relevance ratings
- [x] Project commands are discovered from actual project files
- [x] Constraints → Strategy → Design → Implementation path is logical
- [x] Architecture pattern is clearly stated with rationale
- [x] Every component in diagram has directory mapping
- [x] Every interface has specification
- [x] Error handling covers all error types
- [x] Quality requirements are specific and measurable
- [x] Every quality requirement has test coverage
- [x] **All architecture decisions confirmed by user** ✅ 4/4 ADRs confirmed
- [x] Component names consistent across diagrams
- [x] A developer could implement from this design

---

## Constraints

CON-1 **Zero-downtime requirement**: Production must remain stable during removal. Marketing SMS is deprecated but transactional SMS (service/buy texts, two-way chat) is business-critical.

CON-2 **Database integrity**: Foreign key constraints exist between marketing tables and events table. Must handle event_integrations records gracefully.

CON-3 **Multi-store architecture**: Tables exist in both central DB (`kiosk_buykiosk`) and per-store DBs (`kiosk_[typeNum]`). Removal must happen across all store databases.

CON-4 **No data loss**: Customer opt-out data (`loyaltyDoNotTextList`) and transactional logs must be preserved. Marketing data can be dropped.

CON-5 **Background processing**: Multiple cron jobs and TaskEngine jobs reference marketing code. Must disable before code removal to prevent errors.

## Implementation Context

### Required Context Sources

```yaml
# Internal documentation and patterns
- doc: CLAUDE.md
  relevance: HIGH
  why: "Project structure, migration system, testing commands"

- doc: docs/specs/028-text-marketing-removal/product-requirements.md
  relevance: CRITICAL
  why: "Requirements this SDD must satisfy"

# Source code files - MARKETING (TO REMOVE)
- file: userfrosting/src/BuyerKiosk/SellerMarketing/
  relevance: CRITICAL
  why: "Entire namespace to be removed - 8 PHP files"

- file: userfrosting/routes/groups/sellermarketing.php
  relevance: HIGH
  why: "35 API endpoints to be removed"

- file: userfrosting/routes/sellermarketing-ui.php
  relevance: HIGH
  why: "3 UI routes to be removed"

- file: userfrosting/src/BuyerKiosk/TaskEngine/Jobs/SmsQueueJob.php
  relevance: HIGH
  why: "TaskEngine job referencing marketing classes - must remove"

- file: userfrosting/src/BuyerKiosk/EventManagement/Adapters/SmsAdapter.php
  relevance: HIGH
  why: "Event Management integration with marketing - must update"

# Source code files - TRANSACTIONAL (TO PRESERVE)
- file: userfrosting/src/BuyerKiosk/SMS/
  relevance: CRITICAL
  why: "Transactional SMS - MUST NOT TOUCH"

- file: userfrosting/src/BuyerKiosk/Chat/
  relevance: CRITICAL
  why: "Two-way SMS chat - MUST NOT TOUCH"

- file: userfrosting/routes/groups/sms.php
  relevance: HIGH
  why: "Contains transactional SMS routes to preserve"
```

### Implementation Boundaries

- **Must Preserve**:
  - `userfrosting/src/BuyerKiosk/SMS/` - Transactional SMS (TextMessageService, Twilio, Vonage)
  - `userfrosting/src/BuyerKiosk/Chat/` - Two-way SMS chat system
  - `userfrosting/src/BuyerKiosk/StaffChat/` - Internal staff messaging
  - `userfrosting/routes/groups/sms.php` - Transactional SMS routes
  - `userfrosting/routes/chat/webhooks.php` - Chat webhook handlers
  - `loyaltyDoNotTextList` table - Customer opt-out preferences
  - `floodProtector` table - Rate limiting
  - All `chat_*` tables - Two-way messaging

- **Can Modify**:
  - `userfrosting/src/BuyerKiosk/EventManagement/` - Remove SMS integration type support
  - Sidebar/navigation templates - Remove marketing menu items
  - Database migration system - Add removal migrations

- **Must Not Touch**:
  - Twilio/Vonage API credentials and configuration
  - Store SMS settings (`Store::getTwilioPhone()`, etc.)
  - Customer data beyond marketing-specific tables

### External Interfaces

#### System Context Diagram

```mermaid
graph TB
    subgraph "TO REMOVE"
        SellerMarketing[SellerMarketing Namespace]
        MarketingRoutes[Marketing API Routes]
        MarketingUI[Marketing UI Pages]
        MarketingJobs[TaskEngine Jobs]
        MarketingWebhooks[Delivery Webhooks]
    end

    subgraph "TO PRESERVE"
        SMS[SMS/ Namespace]
        Chat[Chat/ Namespace]
        TransRoutes[Transactional Routes]
        ChatWebhooks[Chat Webhooks]
    end

    subgraph "External Services"
        Twilio[Twilio API]
        Vonage[Vonage API]
    end

    SellerMarketing --> Twilio
    SellerMarketing --> Vonage
    MarketingWebhooks --> SellerMarketing

    SMS --> Twilio
    SMS --> Vonage
    Chat --> Vonage
    ChatWebhooks --> Chat
    TransRoutes --> SMS
```

#### Interface Specifications

```yaml
# Routes to REMOVE
removed_routes:
  - name: "SellerMarketing API"
    path: /api/:typeNum/seller-marketing/*
    file: userfrosting/routes/groups/sellermarketing.php
    endpoints: 35

  - name: "SellerMarketing UI"
    path: /admin/:typeNum/seller-marketing/*
    file: userfrosting/routes/sellermarketing-ui.php
    endpoints: 3

# Webhooks to REMOVE
removed_webhooks:
  - name: "Twilio Marketing Delivery"
    path: /api/webhooks/twilio-delivery.php
    updates: seller_marketing_queue

  - name: "Vonage Marketing Delivery"
    path: /api/webhooks/vonage-delivery.php
    updates: seller_marketing_queue

# Routes to PRESERVE
preserved_routes:
  - name: "SMS Inbound"
    path: /api/sms/inbound
    file: userfrosting/routes/groups/sms.php
    purpose: "Opt-out handling, customer registration"

  - name: "SMS Delivery"
    path: /api/sms/delivery
    file: userfrosting/routes/groups/sms.php
    purpose: "Transactional delivery tracking via LoyaltyOutgoing"

  - name: "Chat Webhooks"
    path: /api/webhooks/chat-inbound
    file: userfrosting/routes/chat/webhooks.php
    purpose: "Two-way SMS chat"
```

### Project Commands

```bash
# Testing
./test.sh                           # Run all tests
./test.sh --testsuite unit          # Run unit tests only
./test.sh --testsuite integration   # Run integration tests

# Database Migrations
php userfrosting/conductor run      # Run pending migrations

# TaskEngine (must disable before removal)
php userfrosting/bin/task job:list  # List all registered jobs
php userfrosting/bin/task worker:manager --status  # Check worker status

# Cron/Jobby Status
launchctl list | grep buyerkiosk    # Check running workers (macOS)
```

## Solution Strategy

- **Architecture Pattern**: Surgical Deprecation with Phased Removal

- **Integration Approach**:
  1. Disable background processing first (prevents errors during code removal)
  2. Remove routes second (returns 404 for old URLs)
  3. Remove code third (clean namespace removal)
  4. Update EventManagement adapter (graceful degradation)
  5. Database table removal last (after code is clean)

- **Justification**: This order ensures:
  - No runtime errors from missing dependencies
  - Users get 404 instead of 500 errors
  - Database integrity maintained until code is clean
  - Rollback possible at each phase

- **Key Decisions**:
  - Tables will be DROPPED, not archived (per PRD - Won't Have data migration)
  - EventManagement adapter will throw IntegrationException for SMS types
  - Existing event_integrations with SMS types will be orphaned but harmless

## Building Block View

### Components

```mermaid
graph TB
    subgraph "Phase 1: Disable Processing"
        SmsQueueJob[SmsQueueJob.php]
        JobbyConfig[jobby-sms.php]
        TaskerScripts[tasker/*.php]
    end

    subgraph "Phase 2: Remove Routes"
        APIRoutes[sellermarketing.php]
        UIRoutes[sellermarketing-ui.php]
        Webhooks[api/webhooks/*.php]
        Sidebar[Navigation Menu]
    end

    subgraph "Phase 3: Remove Code"
        Namespace[SellerMarketing/]
        MockGen[SellerMarketingMockGenerator]
        Workers[sms-queue-worker.php]
        Scripts[scripts/run-sms-queue.php]
    end

    subgraph "Phase 4: Update EventMgmt"
        SmsAdapter[SmsAdapter.php]
        EventIntegration[EventIntegration.php]
    end

    subgraph "Phase 5: Database"
        CentralTables[seller_marketing_queue]
        StoreTables[seller_marketing_* per store]
        FKCleanup[event_integrations cleanup]
    end

    SmsQueueJob --> APIRoutes
    APIRoutes --> Namespace
    Namespace --> SmsAdapter
    SmsAdapter --> CentralTables
```

### Directory Map

**Files to REMOVE:**

```
userfrosting/
├── src/BuyerKiosk/
│   ├── SellerMarketing/                    # REMOVE: Entire directory
│   │   ├── BaseTrigger.php
│   │   ├── DaysSinceSoldTrigger.php
│   │   ├── SellerMarketingTriggerProcessor.php
│   │   ├── SmsQueue.php
│   │   ├── SmsWorker.php
│   │   ├── TriggerProcessor.php
│   │   ├── Controllers/
│   │   │   └── SellerMarketingController.php
│   │   └── Triggers/
│   │       └── TriggerInterface.php
│   ├── TaskEngine/Jobs/
│   │   └── SmsQueueJob.php                 # REMOVE: TaskEngine job
│   └── Mock/Generators/
│       └── SellerMarketingMockGenerator.php # REMOVE: Mock generator
├── routes/
│   ├── groups/
│   │   └── sellermarketing.php             # REMOVE: API routes
│   └── sellermarketing-ui.php              # REMOVE: UI routes
├── workers/
│   └── sms-queue-worker.php                # REMOVE: Worker script
├── scripts/
│   ├── run-sms-queue.php                   # REMOVE: Manual runner
│   ├── test-delivery-tracking.php          # REMOVE: Test script
│   ├── test-twilio-delivery-tracking.php   # REMOVE: Test script
│   └── test-live-delivery-tracking.php     # REMOVE: Test script
├── config/
│   └── jobby-sms.php                       # REMOVE: Scheduler config

tasker/
├── process-sms-triggers.php                # REMOVE: Cron script
├── process-sms-queue.php                   # REMOVE: Cron script
├── process-triggers.php                    # REMOVE: Cron script
└── queue-pusher.php                        # REMOVE: Queue helper

public_html/api/webhooks/
├── twilio-delivery.php                     # REMOVE: Marketing webhook
├── twilio-delivery-simple.php              # REMOVE: Marketing webhook
├── vonage-delivery.php                     # REMOVE: Marketing webhook
└── vonage-delivery-simple.php              # REMOVE: Marketing webhook

templates/themes/default/sellermarketing/   # REMOVE: All templates
```

**Files to MODIFY:**

```
userfrosting/
├── src/BuyerKiosk/
│   ├── EventManagement/
│   │   ├── Adapters/SmsAdapter.php         # MODIFY: Return error for create()
│   │   └── Models/EventIntegration.php     # MODIFY: Remove SMS type constants
│   └── TaskEngine/Commands/
│       └── TaskCommandFactory.php          # MODIFY: Remove SmsQueueJob registration
└── templates/themes/default/
    └── [sidebar templates]                 # MODIFY: Remove marketing menu items
```

### Interface Specifications

#### Data Storage Changes

```yaml
# Tables to DROP (per store database)
Table: seller_marketing_messages
  action: DROP TABLE
  migration: 028_01_drop_seller_marketing_messages.json

Table: seller_marketing_triggers
  action: DROP TABLE
  migration: 028_02_drop_seller_marketing_triggers.json

Table: seller_marketing_blasts
  action: DROP TABLE
  migration: 028_03_drop_seller_marketing_blasts.json

Table: seller_marketing_analytics
  action: DROP TABLE
  migration: 028_04_drop_seller_marketing_analytics.json

Table: seller_marketing_customer_log
  action: DROP TABLE
  migration: 028_05_drop_seller_marketing_customer_log.json

Table: sms_queue (marketing-specific)
  action: DROP TABLE (if exists - not all stores have this)
  migration: 028_06_drop_sms_queue.json

Table: trigger_last_run
  action: DROP TABLE
  migration: 028_07_drop_trigger_last_run.json

# Tables to DROP (central database)
Table: seller_marketing_queue
  action: DROP TABLE
  database: kiosk_buykiosk
  migration: 028_08_drop_seller_marketing_queue_central.json

# Tables to CLEAN (central database)
Table: event_integrations
  action: DELETE WHERE integrationType IN ('sms_blast', 'sms_trigger')
  migration: 028_09_cleanup_event_integrations.json

Table: eventTemplate_Integrations
  action: DELETE WHERE integrationType IN ('sms_blast', 'sms_trigger')
  migration: 028_10_cleanup_event_template_integrations.json
```

#### Internal API Changes

```yaml
# EventManagement SmsAdapter modification
Endpoint: SmsAdapter::create()
  Current: Creates seller_marketing_blasts or seller_marketing_triggers record
  After: Throws IntegrationException("SMS marketing integrations have been deprecated")

Endpoint: SmsAdapter::validateConfig()
  Current: Validates messageId and config
  After: Returns ['SMS marketing integrations are no longer supported']

Endpoint: SmsAdapter::getStatus()
  Current: Queries seller_marketing tables
  After: Returns ['status' => 'deprecated', 'details' => []]
```

## Runtime View

### Primary Flow: Route Removal

1. User navigates to `/admin/:typeNum/seller-marketing/smart-messaging`
2. Route file is removed, Slim returns 404
3. User sees standard 404 page
4. No backend errors logged (route simply doesn't exist)

### Error Handling

- **Old marketing URLs**: Return 404 (route file removed)
- **API calls to marketing endpoints**: Return 404 (route file removed)
- **EventManagement SMS integration attempt**: Throws `IntegrationException` with clear message
- **Background job references**: Jobs removed from TaskCommandFactory before code removal

## Deployment View

### Single Application Deployment

- **Environment**: Production servers running PHP 8.x
- **Configuration**: No new env vars required; removal only
- **Dependencies**: No new dependencies
- **Performance**: Reduced load from disabled background jobs

### Deployment Order (Single Deployment)

Since the feature never reached production, all changes can be deployed together:

```
Single Deployment Checklist:
├── 1. Delete SellerMarketing/ namespace directory
├── 2. Delete SmsQueueJob.php from TaskEngine/Jobs/
├── 3. Remove SmsQueueJob registration from TaskCommandFactory.php
├── 4. Delete route files (sellermarketing.php, sellermarketing-ui.php)
├── 5. Delete webhook files from public_html/api/webhooks/
├── 6. Delete worker scripts (sms-queue-worker.php, etc.)
├── 7. Delete tasker/ marketing scripts
├── 8. Delete SellerMarketingMockGenerator.php
├── 9. Modify SmsAdapter to throw deprecation errors
├── 10. Remove sidebar menu items (if any exist)
├── 11. Run database migrations (DROP TABLEs)
└── 12. Verify: Tests pass, no PHP errors
```

**Note**: No cron jobs to disable since feature never went live.

## Cross-Cutting Concepts

### Error Handling

```yaml
# Deprecated feature access
scenario: User bookmarks marketing URL
handling:
  - Route file removed → Slim returns 404 automatically
  - No special redirect needed (per PRD)

# EventManagement integration
scenario: Code tries to create SMS integration
handling:
  - SmsAdapter::create() throws IntegrationException
  - Message: "SMS marketing integrations have been deprecated"
  - Event creation succeeds without SMS integration

# Database constraints
scenario: Foreign key references marketing tables
handling:
  - event_integrations cleaned before table drops
  - CASCADE on seller_marketing_* internal FKs handles cleanup
```

### Security Concerns

```yaml
# Found during analysis - should be addressed in this removal
security_issues:
  - file: public_html/api/webhooks/twilio-delivery-simple.php
    issue: Hardcoded database credentials (line 80-82)
    action: File will be deleted, resolving the issue

  - file: tasker/process-sms-triggers.php
    issue: Hardcoded SendGrid API key (line 574)
    action: File will be deleted, resolving the issue

  - file: tasker/model/types/smsTask.php
    issue: Hardcoded database credentials (line 19-20)
    action: File will be deleted, resolving the issue
```

## Architecture Decisions

- [x] ADR-1 **Drop Tables Directly**: DROP all marketing tables without archiving
  - Rationale: Feature never reached production - no data exists to preserve
  - Trade-offs: None - no production data to lose
  - User confirmed: ✅ 2025-01-13

- [x] ADR-2 **EventManagement Throws Exception**: SmsAdapter throws exception instead of silent failure
  - Rationale: Clear error message helps debugging; prevents silent corruption
  - Trade-offs: Any code still referencing SMS integration will fail loudly (good for catching issues)
  - User confirmed: ✅ 2025-01-13

- [x] ADR-3 **No Redirects for Old URLs**: Return 404 instead of redirecting to dashboard
  - Rationale: Simple implementation; no users have bookmarks since feature never launched
  - Trade-offs: None - no active users of the feature
  - User confirmed: ✅ 2025-01-13

- [x] ADR-4 **Single Deployment**: All changes deployed together
  - Rationale: Feature never reached production, so lower risk; faster to implement
  - Trade-offs: Less granular rollback, but acceptable given no production usage
  - User confirmed: ✅ 2025-01-13

## Quality Requirements

- **Stability**: Zero production errors related to removal within 7 days post-deployment
- **Transactional SMS**: 100% of service/buy texts continue working (verified by manual test)
- **Two-way Chat**: 100% of inbound SMS handling continues working (verified by test message)
- **Performance**: No degradation in response times; reduced background load

## Risks and Technical Debt

### Known Technical Issues

- **Incomplete SellerMarketing implementation**: Controller references 5 non-existent classes (SellerMarketingMessage, SellerMarketingTrigger, etc.) - suggests feature was never fully completed
- **Duplicate processor classes**: Both `TriggerProcessor.php` and `SellerMarketingTriggerProcessor.php` exist with overlapping functionality
- **Hardcoded credentials**: Multiple files contain hardcoded database/API credentials (will be removed with files)

### Implementation Gotchas

- **Multi-store migrations**: Must run DROP TABLE migrations on every store database, not just central
- **TaskEngine registration**: SmsQueueJob is registered in TaskCommandFactory.php line 241 - easy to miss
- **Jobby scheduler**: jobby-sms.php is separate from main jobby.php - has its own cron entry
- **Legacy tasker scripts**: Some stores may have custom cron entries pointing to tasker/ scripts

## Test Specifications

### Critical Test Scenarios

**Scenario 1: Marketing Route Returns 404**
```gherkin
Given: Marketing routes have been removed
When: User navigates to /admin/ou00/seller-marketing/smart-messaging
Then: HTTP 404 response is returned
And: No PHP errors in error log
```

**Scenario 2: Transactional SMS Still Works**
```gherkin
Given: Marketing code has been removed
And: Customer has a buy ready for pickup
When: Store sends buy notification SMS
Then: SMS is sent successfully via TextMessageService
And: Delivery tracking works via /api/sms/delivery
```

**Scenario 3: Two-Way Chat Still Works**
```gherkin
Given: Marketing code has been removed
And: Customer sends SMS to store shortcode
When: Vonage/Twilio webhook fires to /api/webhooks/chat-inbound
Then: Message is routed to correct store
And: Chat thread is created/updated
And: STOP command opts out customer
```

**Scenario 4: Event Creation Without SMS**
```gherkin
Given: SmsAdapter has been modified to throw deprecation error
When: User creates new event
And: Event does not include SMS integration
Then: Event is created successfully
And: No errors related to SMS adapter
```

**Scenario 5: Database Integrity After Removal**
```gherkin
Given: All marketing tables have been dropped
When: Running full application test suite
Then: No foreign key constraint errors
And: Event system queries succeed
And: Customer queries succeed
```

### Test Coverage Requirements

- **Transactional SMS**: Manual test of sendBuyText() and sendServiceText()
- **Chat Webhooks**: Manual test of inbound SMS handling
- **Event System**: Automated tests pass without SMS integration
- **Route Removal**: 404 responses for all marketing endpoints
- **Database**: No FK errors after migrations run

---

## Glossary

### Domain Terms

| Term | Definition | Context |
|------|------------|---------|
| Text Blast | One-time bulk SMS campaign sent to customer segment | Being removed |
| SMS Trigger | Automated SMS sent based on customer behavior (e.g., days since purchase) | Being removed |
| Transactional SMS | Service/buy notifications sent to individual customers | Being preserved |
| Two-way Chat | Interactive SMS conversation between store and customer | Being preserved |

### Technical Terms

| Term | Definition | Context |
|------|------------|---------|
| SellerMarketing | PHP namespace containing all marketing SMS code | `userfrosting/src/BuyerKiosk/SellerMarketing/` |
| SmsQueueJob | TaskEngine job that processes marketing SMS queue | To be removed |
| TextMessageService | Service class for sending transactional SMS | Must preserve |
| LoyaltyOutgoing | Model for transactional SMS delivery tracking | Must preserve |
