# Phase 1 — Foundation & Infrastructure
## Weeks 1–2 | Complete these tasks in order. Commit after each section.

---

## STEP 1.0 — Laravel Project Setup

```bash
composer create-project laravel/laravel:^11.0 .
composer require livewire/livewire
composer require laravel/sanctum
npm install -D tailwindcss @tailwindcss/forms @tailwindcss/typography autoprefixer postcss
npm install alpinejs
npx tailwindcss init -p
```

Install all packages at once:
```bash
composer require \
  maatwebsite/excel \
  barryvdh/laravel-dompdf \
  intervention/image \
  spatie/laravel-medialibrary \
  spatie/laravel-activitylog \
  spatie/laravel-permission \
  pragmarx/google2fa-laravel \
  bacon/bacon-qr-code \
  facebook/graph-sdk \
  google/apiclient \
  guzzlehttp/guzzle

composer require --dev laravel/telescope laravel/pint pestphp/pest
```

---

## STEP 1.1 — Master Layout (Hootsuite UI)

Create `resources/views/layouts/app.blade.php`:

**Layout structure:**
- Full-height flex layout
- Left sidebar: `w-16` icon-only (collapsed) or `w-56` (expanded), `bg-[#1b2a4a]`
- Sidebar items (with Heroicons): Dashboard, Streams, Composer, Calendar, Library, Analytics, Inbox, Settings
- Active state: `bg-[#2d4a7a]` with left border `border-l-2 border-[#f5a623]`
- Top bar: white, height 14, logo left, notifications bell + user avatar right
- Main content: `bg-gray-50 flex-1 overflow-auto`
- Include `@livewireScripts`, `@vite`, Alpine.js

Create `resources/views/layouts/auth.blade.php` for login/register pages (centered card, dark blue gradient bg).

---

## STEP 1.2 — Authentication System

### Migrations
```php
// Add to users table migration:
$table->string('two_factor_secret')->nullable();
$table->boolean('two_factor_enabled')->default(false);
$table->timestamp('two_factor_confirmed_at')->nullable();
$table->integer('login_attempts')->default(0);
$table->timestamp('locked_until')->nullable();
$table->rememberToken();
```

### Controllers & Routes
- `AuthController` with: `showLogin`, `login`, `logout`, `showRegister` (disabled — single user, redirect to login)
- `TwoFactorController` with: `show`, `verify`, `enable`, `disable`, `generateQr`
- `ForgotPasswordController`, `ResetPasswordController`

### Authentication Logic in `AuthController@login`:
```php
// 1. Validate credentials
// 2. Check if account is locked (locked_until > now())
// 3. Attempt login
// 4. On failure: increment login_attempts, lock after 5 attempts for 15 minutes
// 5. On success: reset attempts, check if 2FA enabled
// 6. If 2FA enabled: store user ID in session, redirect to /2fa/verify
// 7. If 2FA disabled: complete login, redirect to dashboard
```

### 2FA Implementation
Use `pragmarx/google2fa-laravel`:
```php
// TwoFactorController@verify
$valid = Google2FA::verifyKey($user->two_factor_secret, $request->code);
// On success: mark session as 2fa_verified, complete login
```

### Livewire Components needed:
- `Auth\LoginForm` — email/password form with remember me, lockout countdown timer
- `Auth\TwoFactorForm` — 6-digit OTP input with auto-submit on 6 digits (Alpine.js)
- `Auth\TwoFactorSetup` — shows QR code + secret, verify step before enabling

### Views:
- `resources/views/auth/login.blade.php`
- `resources/views/auth/2fa.blade.php`
- `resources/views/auth/passwords/email.blade.php`
- `resources/views/auth/passwords/reset.blade.php`

---

## STEP 1.3 — Social Account OAuth Manager

### Migration: `social_accounts`
```php
Schema::create('social_accounts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('platform'); // Enum: instagram, facebook, twitter, linkedin, tiktok, youtube, pinterest, threads
    $table->string('platform_account_id');
    $table->string('account_name');
    $table->string('account_avatar')->nullable();
    $table->text('access_token'); // encrypted
    $table->text('refresh_token')->nullable(); // encrypted
    $table->timestamp('token_expires_at')->nullable();
    $table->string('status')->default('active'); // active, expired, error, disconnected
    $table->json('scopes')->nullable();
    $table->json('meta')->nullable(); // extra platform data
    $table->timestamps();
    $table->unique(['user_id', 'platform', 'platform_account_id']);
});
```

### Model: `SocialAccount`
```php
// Encrypt/decrypt tokens automatically using casts
protected $casts = [
    'access_token' => 'encrypted',
    'refresh_token' => 'encrypted',
    'scopes' => 'array',
    'meta' => 'array',
    'token_expires_at' => 'datetime',
];

// Helper methods
public function isExpired(): bool
public function isExpiringSoon(int $days = 7): bool
public function getPlatformLabelAttribute(): string
public function getPlatformColorAttribute(): string
public function getPlatformIconAttribute(): string
```

### Platform Enum: `app/Enums/Platform.php`
```php
enum Platform: string {
    case INSTAGRAM = 'instagram';
    case FACEBOOK = 'facebook';
    case TWITTER = 'twitter';
    case LINKEDIN = 'linkedin';
    case TIKTOK = 'tiktok';
    case YOUTUBE = 'youtube';
    case PINTEREST = 'pinterest';
    case THREADS = 'threads';

    public function label(): string { ... }
    public function color(): string { ... }  // hex colors matching each platform brand
    public function maxChars(): int { ... }  // 280, 3000, 2200, etc.
    public function supportsVideo(): bool { ... }
    public function supportsCarousel(): bool { ... }
}
```

### OAuth Routes (web.php)
```
GET  /social/connect/{platform}          → SocialAuthController@redirect
GET  /social/callback/{platform}         → SocialAuthController@callback
POST /social/disconnect/{account}        → SocialAuthController@disconnect
POST /social/reconnect/{account}         → SocialAuthController@reconnect
GET  /social/refresh-token/{account}     → SocialAuthController@refreshToken (internal)
```

### `SocialAuthController` — implement redirect + callback for each platform:

**Meta (Instagram + Facebook):** Use `facebook/graph-sdk`
```php
// Scopes: pages_read_engagement, pages_manage_posts, instagram_basic, 
//         instagram_content_publish, instagram_manage_comments
$fb = new Facebook(['app_id' => config('services.meta.app_id'), ...]);
$helper = $fb->getRedirectLoginHelper();
$url = $helper->getLoginUrl(config('services.meta.redirect_uri'), $permissions);
```

**Twitter:** OAuth 1.0a — use `noweh/twitter-api-v2`
```php
// Store oauth_token + oauth_verifier in session during callback
```

**LinkedIn:** OAuth 2.0 — manual Guzzle calls to `https://www.linkedin.com/oauth/v2`
```php
// Scopes: r_liteprofile, r_emailaddress, w_member_social
```

**Google (YouTube):** Use `google/apiclient`
```php
// Scopes: https://www.googleapis.com/auth/youtube.upload, .readonly
```

**TikTok, Pinterest:** Manual OAuth 2.0 via Guzzle

### Service: `app/Services/Social/TokenRefreshService.php`
```php
// Called by scheduled command daily
// Checks all accounts where token_expires_at < now() + 7 days
// Attempts refresh using platform-specific refresh flow
// Updates token or marks account as 'expired'
// Fires TokenExpiringNotification if within 7 days and refresh fails
```

### Livewire Component: `SocialAccounts\AccountManager`
**UI (matches Hootsuite Social Accounts screen):**
- Two tabs: "Manage Accounts" | "Connect Accounts"
- **Connect tab:** 8-platform grid, each with platform logo, name, and "Connect [Platform]" button
- **Manage tab:** List of connected accounts with:
  - Platform icon + account name + avatar
  - Status badge (Active = green, Expired = red, Expiring Soon = amber)
  - "Disconnect" button (confirm modal)
  - Token expiry date shown if within 14 days

---

## STEP 1.4 — Core Database Migrations

Run all migrations from the schema reference in CLAUDE.md in this order.

For each model, create:
- Migration
- Eloquent Model with `$fillable`, `$casts`, relationships
- Factory for testing

Key model relationships:
```php
// User
hasMany(SocialAccount::class)
hasMany(Content::class)
hasMany(Post::class)
hasOne(BrandKit::class)

// Content
belongsTo(User::class)
hasMany(Post::class)
belongsToMany(MediaFile::class, 'content_media')

// Post
belongsTo(Content::class)
belongsTo(SocialAccount::class)
hasMany(PostLog::class)
```

### Enums to create:
```
app/Enums/Platform.php         (done above)
app/Enums/PostStatus.php       (draft, scheduled, publishing, published, failed, needs_review)
app/Enums/MediaType.php        (image, video, gif, ai_image, ai_video)
app/Enums/MessageType.php      (comment, dm, mention)
app/Enums/SentimentType.php    (positive, neutral, negative)
app/Enums/AccountStatus.php    (active, expired, expiring_soon, error, disconnected)
```

---

## STEP 1.5 — File Storage System

### Config: `config/mediaforge.php`
```php
return [
    'disk' => env('FILESYSTEM_DISK', 'local'),
    'max_image_size' => 8 * 1024 * 1024, // 8MB
    'max_video_size' => 512 * 1024 * 1024, // 512MB
    'allowed_image_types' => ['image/jpeg', 'image/png', 'image/webp', 'image/gif'],
    'allowed_video_types' => ['video/mp4', 'video/quicktime', 'video/x-msvideo'],
    'platform_limits' => [
        'instagram' => ['image' => 8388608, 'video' => 104857600],
        'twitter' => ['image' => 5242880, 'video' => 536870912],
        // etc.
    ],
];
```

### Service: `app/Services/Media/FileUploadService.php`
```php
public function upload(UploadedFile $file, string $type): MediaFile
// 1. Validate MIME type
// 2. Check file size against platform limits
// 3. Generate unique filename: {date}/{uuid}.{ext}
// 4. Store to configured disk
// 5. Generate thumbnail (for videos: extract frame 0 using FFmpeg)
// 6. For images: compress using Intervention Image
// 7. Create and return MediaFile model
```

### Service: `app/Services/Media/ThumbnailService.php`
```php
// For images: resize to 400x400 maintaining aspect ratio
// For videos: FFmpeg extract first frame
public function generate(MediaFile $media): string
```

---

## STEP 1.6 — Scheduled Commands & Notifications

### Commands to register in `app/Console/Kernel.php` (or `routes/console.php` for Laravel 11):
```php
Schedule::command('social:refresh-tokens')->daily();
Schedule::command('posts:publish-scheduled')->everyMinute();
Schedule::command('analytics:sync')->dailyAt('02:00');
Schedule::command('rss:poll-feeds')->hourly();
Schedule::command('ai:auto-distribute')->hourly();
```

### Notifications:
- `TokenExpiringNotification` — via mail + database channel
- `PostPublishedNotification` — via database channel
- `PostFailedNotification` — via mail + database channel
- `DailySummaryNotification` — via mail (scheduled)

---

## STEP 1.7 — Dashboard Home (Livewire)

**Route:** `GET /dashboard` → `DashboardController@index`

**Livewire Component: `Dashboard\DashboardHome`**

UI matches Hootsuite Streams view:
- **Top section:** 4 metric cards: Total Posts This Week, Total Engagement, Connected Accounts, Upcoming Posts
- **Streams section (bottom half):** 4-column card grid:
  - Column 1: "Upcoming Scheduled Posts" list (next 5 posts with platform icon, time, caption snippet, status badge)
  - Column 2: "Recent Activity" (post logs / notifications feed)
  - Column 3: "Analytics Overview" (followers total, engagement this week — Chart.js mini bar chart)
  - Column 4: "Notifications" (token expiry warnings, failed posts)
- **Right side panel:** Quick "+ New Post" button (amber, opens composer modal)

---

## STEP 1.8 — Tests

Create Pest tests:
```
tests/Feature/Auth/LoginTest.php          — login, lockout, 2FA flow
tests/Feature/Auth/TwoFactorTest.php      — enable/disable, verify OTP
tests/Feature/Social/OAuthCallbackTest.php — mock OAuth responses
tests/Unit/Services/TokenRefreshTest.php  — token refresh logic
tests/Unit/Enums/PlatformTest.php         — enum methods
```

---

## COMPLETION CHECKLIST
- [ ] Laravel project created with all packages installed
- [ ] Master layout (app.blade.php) matches Hootsuite dark sidebar UI
- [ ] Login with lockout + 2FA (Google Authenticator) working
- [ ] Password reset via email working
- [ ] All 8 social platform OAuth flows scaffolded
- [ ] Token refresh command working
- [ ] All 16 migrations created and run
- [ ] All Eloquent models with relationships
- [ ] All PHP 8.1 Enums created
- [ ] File upload service with thumbnail + compression
- [ ] Dashboard home Livewire component built
- [ ] All scheduled commands registered
- [ ] Notifications created
- [ ] Pest tests passing

When all checked: update `docs/current_phase.txt` to `2`, commit with message `feat(phase1): complete foundation and infrastructure`, then stop.
