<?php

namespace App\Jobs;

use App\Models\Brand;
use App\Models\SocialAccount;
use App\Models\SocialAggregate;
use App\Models\SocialPost;
use App\Models\SocialSyncError;
use App\Services\SocialProviders\ProviderFactory;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\DB;

class SyncBrandSocialAccounts implements ShouldQueue
{
    use Queueable;

    /**
     * The brand to sync.
     *
     * @var Brand
     */
    protected $brand;

    /**
     * The specific account to sync.
     *
     * @var SocialAccount|null
     */
    protected $account;

    /**
     * Create a new job instance.
     *
     * @param Brand|SocialAccount $brandOrAccount
     * @return void
     */
    public function __construct($brandOrAccount = null)
    {
        if ($brandOrAccount instanceof Brand) {
            $this->brand = $brandOrAccount;
        } elseif ($brandOrAccount instanceof SocialAccount) {
            $this->account = $brandOrAccount;
        }
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle(): void
    {
        // Determine which accounts to sync
        $accounts = $this->getAccountsToSync();

        // Sync each account
        foreach ($accounts as $account) {
            $this->syncAccount($account);
        }

        // Update aggregates after syncing all accounts
        if ($this->brand) {
            $this->updateAggregates();
        } elseif ($this->account) {
            $this->updateAggregatesForAccount($this->account);
        }
    }

    /**
     * Get the accounts to sync.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    protected function getAccountsToSync()
    {
        if ($this->account) {
            return collect([$this->account]);
        }

        if ($this->brand) {
            return SocialAccount::where('brand_id', $this->brand->id)->get();
        }

        return collect();
    }

    /**
     * Sync a single account.
     *
     * @param SocialAccount $account
     * @return void
     */
    protected function syncAccount(SocialAccount $account)
    {
        // For demo accounts, just update the last synced time
        if ($account->is_demo) {
            $account->last_synced_at = now();
            $account->save();
            return;
        }

        try {
            // Get the provider instance
            $provider = ProviderFactory::make($account->provider);

            // Fetch account details
            $details = $provider->fetchAccountDetails($account);

            // Update account with details
            if (!empty($details)) {
                $account->meta = array_merge($account->meta ?? [], $details);
            }

            // Fetch recent posts
            $posts = $provider->fetchRecentPosts($account, 20);

            // Process posts
            $this->processPosts($account, $posts);

            // Update last synced time
            $account->last_synced_at = now();
            $account->save();
        } catch (\Exception $e) {
            // Log the error
            SocialSyncError::create([
                'social_account_id' => $account->id,
                'error_message' => $e->getMessage(),
                'error_trace' => $e->getTraceAsString(),
                'occurred_at' => now(),
            ]);
            
            \Log::error('Failed to sync brand account: ' . $e->getMessage(), [
                'account_id' => $account->id,
                'provider' => $account->provider,
                'brand_id' => $account->brand_id,
            ]);
        }
    }

    /**
     * Process posts for an account.
     *
     * @param SocialAccount $account
     * @param array $posts
     * @return void
     */
    protected function processPosts(SocialAccount $account, array $posts)
    {
        foreach ($posts as $postData) {
            // Use database transaction for upsert
            DB::transaction(function () use ($account, $postData) {
                // Find existing post or create new one
                $post = SocialPost::updateOrCreate(
                    [
                        'social_account_id' => $account->id,
                        'provider_post_id' => $postData['id'],
                    ],
                    [
                        'title' => $postData['title'] ?? null,
                        'caption' => $postData['caption'] ?? null,
                        'content' => $postData['text'] ?? $postData['description'] ?? null,
                        'media' => [
                            'thumbnail' => $postData['thumbnail'] ?? $postData['media_url'] ?? $postData['image_url'] ?? null,
                            'video' => $postData['video_url'] ?? null,
                        ],
                        'permalink' => $postData['permalink'] ?? $postData['link'] ?? null,
                        'published_at' => $postData['published_at'] ?? $postData['created_at'] ?? $postData['timestamp'] ?? null,
                        'metrics' => [
                            'views' => $postData['views'] ?? 0,
                            'likes' => $postData['likes'] ?? $postData['ups'] ?? $postData['like_count'] ?? 0,
                            'comments' => $postData['comments'] ?? $postData['comment_count'] ?? 0,
                            'shares' => $postData['shares'] ?? $postData['shares'] ?? 0,
                            'saves' => $postData['saves'] ?? $postData['save_count'] ?? 0,
                        ],
                        'raw_json' => $postData,
                        'last_checked_at' => now(),
                    ]
                );
            });
        }
    }

    /**
     * Update aggregates for the brand.
     *
     * @return void
     */
    protected function updateAggregates()
    {
        $brandId = $this->brand->id;

        // Calculate total followers
        $totalFollowers = SocialAccount::where('brand_id', $brandId)
            ->where('is_demo', false)
            ->sum('meta->followers');

        // For demo accounts, add fake data
        $demoFollowers = SocialAccount::where('brand_id', $brandId)
            ->where('is_demo', true)
            ->sum('meta->followers');

        $totalFollowers += $demoFollowers;

        // Update or create aggregate
        SocialAggregate::updateOrCreate(
            [
                'brand_id' => $brandId,
                'key' => 'total_followers',
            ],
            [
                'value' => [
                    'value' => $totalFollowers,
                    'previous' => $this->getPreviousAggregateValue($brandId, 'total_followers'),
                ],
                'computed_at' => now(),
            ]
        );

        // Calculate total posts
        $totalPosts = SocialPost::whereHas('socialAccount', function ($query) use ($brandId) {
            $query->where('brand_id', $brandId);
        })->count();

        // Update or create aggregate
        SocialAggregate::updateOrCreate(
            [
                'brand_id' => $brandId,
                'key' => 'total_posts',
            ],
            [
                'value' => [
                    'value' => $totalPosts,
                    'previous' => $this->getPreviousAggregateValue($brandId, 'total_posts'),
                ],
                'computed_at' => now(),
            ]
        );

        // Calculate total views
        $totalViews = SocialPost::whereHas('socialAccount', function ($query) use ($brandId) {
            $query->where('brand_id', $brandId);
        })->sum('metrics->views');

        // Update or create aggregate
        SocialAggregate::updateOrCreate(
            [
                'brand_id' => $brandId,
                'key' => 'total_views',
            ],
            [
                'value' => [
                    'value' => $totalViews,
                    'previous' => $this->getPreviousAggregateValue($brandId, 'total_views'),
                ],
                'computed_at' => now(),
            ]
        );

        // Calculate engagement rate
        $totalLikes = SocialPost::whereHas('socialAccount', function ($query) use ($brandId) {
            $query->where('brand_id', $brandId);
        })->sum('metrics->likes');

        $totalComments = SocialPost::whereHas('socialAccount', function ($query) use ($brandId) {
            $query->where('brand_id', $brandId);
        })->sum('metrics->comments');
        
        $totalShares = SocialPost::whereHas('socialAccount', function ($query) use ($brandId) {
            $query->where('brand_id', $brandId);
        })->sum('metrics->shares');
        
        $totalSaves = SocialPost::whereHas('socialAccount', function ($query) use ($brandId) {
            $query->where('brand_id', $brandId);
        })->sum('metrics->saves');

        $engagementRate = 0;
        if ($totalViews > 0) {
            $engagementRate = round((($totalLikes + $totalComments + $totalShares + $totalSaves) / $totalViews) * 100, 2);
        }

        // Update or create aggregate
        SocialAggregate::updateOrCreate(
            [
                'brand_id' => $brandId,
                'key' => 'engagement_rate',
            ],
            [
                'value' => [
                    'value' => $engagementRate,
                    'previous' => $this->getPreviousAggregateValue($brandId, 'engagement_rate'),
                ],
                'computed_at' => now(),
            ]
        );

        // Calculate daily views for the last 14 days
        $this->updateDailyViews($brandId);
    }

    /**
     * Update aggregates for a specific account.
     *
     * @param SocialAccount $account
     * @return void
     */
    protected function updateAggregatesForAccount(SocialAccount $account)
    {
        // This is a simplified version that just calls the main updateAggregates method
        // In a real implementation, you might want to update only account-specific aggregates
        $this->brand = $account->brand;
        $this->updateAggregates();
    }

    /**
     * Get the previous aggregate value.
     *
     * @param int $brandId
     * @param string $key
     * @return mixed
     */
    protected function getPreviousAggregateValue($brandId, $key)
    {
        $aggregate = SocialAggregate::where('brand_id', $brandId)
            ->where('key', $key)
            ->first();

        return $aggregate ? $aggregate->getNumericValue() : null;
    }

    /**
     * Update daily views for the last 14 days.
     *
     * @param int $brandId
     * @return void
     */
    protected function updateDailyViews($brandId)
    {
        // Get posts from the last 14 days
        $posts = SocialPost::whereHas('socialAccount', function ($query) use ($brandId) {
            $query->where('brand_id', $brandId);
        })->where('published_at', '>=', now()->subDays(14))
            ->get();

        // Group by day and sum views
        $dailyViews = [];
        foreach ($posts as $post) {
            $day = $post->published_at->format('Y-m-d');
            if (!isset($dailyViews[$day])) {
                $dailyViews[$day] = 0;
            }
            $dailyViews[$day] += $post->getViewsCount();
        }

        // Update or create aggregates for each day
        foreach ($dailyViews as $day => $views) {
            SocialAggregate::updateOrCreate(
                [
                    'brand_id' => $brandId,
                    'key' => 'daily_views',
                    'computed_at' => $day,
                ],
                [
                    'value' => $views,
                    'computed_at' => $day,
                ]
            );
        }
    }
}