<?php

namespace App\Console\Commands;

use App\Models\SubscribePlan;
use App\Models\User;
use App\Models\UserSubscriber;
use Illuminate\Console\Command;

class AddSubscriptionDays extends Command
{
    protected $signature = 'subscriptions:add-days
                            {days=3 : Number of days to add}
                            {--all-users : Ensure every user gets days even if they have no subscription}
                            {--only-without-subscription : Process only users who do not have any subscription}
                            {--plan-id= : Subscribe plan id to use for users without a subscription}
                            {--include-expired : Also extend expired subscriptions}
                            {--chunk=1000 : Chunk size for processing}
                            {--dry-run : Show how many records would change without saving}';

    protected $description = 'Add days to all user subscriptions';

    public function handle(): int
    {
        $days = (int) $this->argument('days');
        $chunkSize = max(1, (int) $this->option('chunk'));
        $includeExpired = (bool) $this->option('include-expired');
        $allUsers = (bool) $this->option('all-users');
        $onlyWithoutSubscription = (bool) $this->option('only-without-subscription');
        $dryRun = (bool) $this->option('dry-run');

        if ($days <= 0) {
            $this->error('Days must be a positive integer.');
            return self::FAILURE;
        }

        if ($onlyWithoutSubscription && !$allUsers) {
            $this->error('The --only-without-subscription option can only be used with --all-users.');
            return self::FAILURE;
        }

        if ($allUsers) {
            $planId = $this->option('plan-id');
            if (!$planId) {
                $this->error('When using --all-users you must provide --plan-id for users without a subscription.');
                return self::FAILURE;
            }

            $plan = SubscribePlan::find($planId);
            if (!$plan) {
                $this->error('Subscribe plan not found for the given --plan-id.');
                return self::FAILURE;
            }
        }

        if ($allUsers) {
            if ($onlyWithoutSubscription) {
                $this->info("Granting {$days} day(s) only to users without a subscription." . ($dryRun ? ' (dry-run)' : ''));
            } else {
                $this->info("Granting {$days} day(s) to every user." . ($dryRun ? ' (dry-run)' : ''));
            }
        } else {
            $scopeLabel = $includeExpired ? 'all' : 'active';
            $this->info("Extending {$scopeLabel} subscriptions by {$days} day(s)." . ($dryRun ? ' (dry-run)' : ''));
        }

        $touched = 0;
        if ($allUsers) {
            $planId = (int) $this->option('plan-id');
            $isFree = (bool) $plan->is_free;

            User::query()->orderBy('id')->chunkById($chunkSize, function ($users) use (&$touched, $days, $dryRun, $planId, $isFree, $onlyWithoutSubscription) {
                foreach ($users as $user) {
                    $latest = $user->userSubscribers()->orderByDesc('expired_at')->first();

                    if ($onlyWithoutSubscription && $latest) {
                        continue;
                    }

                    $touched++;

                    if ($dryRun) {
                        continue;
                    }

                    if ($latest) {
                        $baseDate = $latest->expired_at && $latest->expired_at->greaterThan(now())
                            ? $latest->expired_at
                            : now();
                        $latest->expired_at = $baseDate->copy()->addDays($days);
                        $latest->save();
                    } else {
                        $user->userSubscribers()->create([
                            'subscribe_plan_id' => $planId,
                            'expired_at' => now()->addDays($days),
                            'is_free' => $isFree,
                        ]);
                    }
                }
            });
        } else {
            $query = UserSubscriber::query();
            if (!$includeExpired) {
                $query->where('expired_at', '>', now());
            }

            $query->orderBy('id')->chunkById($chunkSize, function ($subscribers) use (&$touched, $days, $dryRun) {
                foreach ($subscribers as $subscriber) {
                    $touched++;
                    if ($dryRun) {
                        continue;
                    }

                    if ($subscriber->expired_at === null) {
                        $subscriber->expired_at = now()->addDays($days);
                    } else {
                        $subscriber->expired_at = $subscriber->expired_at->copy()->addDays($days);
                    }

                    $subscriber->save();
                }
            });
        }

        if ($dryRun) {
            $label = $allUsers ? 'user(s)' : 'subscription record(s)';
            $this->info("{$touched} {$label} would be processed.");
        } else {
            $label = $allUsers ? 'user(s)' : 'subscription record(s)';
            $this->info("{$touched} {$label} processed.");
        }

        return self::SUCCESS;
    }
}
