<?php

namespace App\Services;

use App\Models\NotificationLog;
use App\Models\Plan;
use App\Models\User;
use App\Models\UserNotificationPreference;
use Throwable;

final class PlanFeatures
{
    private Plan $plan;

    private function __construct(private readonly User $user)
    {
        try {
            $this->plan = Plan::resolveForUser($user);
        } catch (Throwable) {
            // Never throw — fall back to a free-tier stub if resolution fails.
            $this->plan = new Plan([
                'name' => 'free',
                'features' => [
                    'fuel_types' => ['max' => 1],
                    'email' => ['enabled' => true, 'frequency' => 'weekly_digest'],
                    'push' => ['enabled' => false, 'frequency' => 'none'],
                    'whatsapp' => ['enabled' => false, 'daily_limit' => 0, 'scheduled_updates' => 0],
                    'sms' => ['enabled' => false, 'daily_limit' => 0],
                    'ai_predictions' => false,
                    'price_threshold' => false,
                    'score_alerts' => false,
                ],
            ]);
        }
    }

    public static function for(User $user): self
    {
        return new self($user);
    }

    /**
     * Channels allowed for a given trigger type, filtered by:
     * tier allows → user has enabled → daily limit not hit.
     *
     * @return string[]
     */
    public function channelsFor(string $triggerType): array
    {
        $allChannels = ['email', 'push', 'whatsapp', 'sms'];
        $allowed = [];

        foreach ($allChannels as $channel) {
            if (! $this->canUseChannel($channel)) {
                continue;
            }

            if (! $this->userHasEnabledChannel($channel)) {
                continue;
            }

            if (! $this->canSendNow($channel)) {
                continue;
            }

            $allowed[] = $channel;
        }

        return $allowed;
    }

    /** Whether the plan allows this channel at all. */
    public function canUseChannel(string $channel): bool
    {
        return (bool) ($this->feature($channel, 'enabled') ?? false);
    }

    /** Read a nested feature value, e.g. feature('sms', 'daily_limit'). */
    private function feature(string $channel, string $key): mixed
    {
        $features = $this->plan->features ?? [];

        return $features[$channel][$key] ?? null;
    }

    /** Whether the user has opted in to this channel for at least one fuel type. */
    private function userHasEnabledChannel(string $channel): bool
    {
        return UserNotificationPreference::where('user_id', $this->user->id)
            ->where('channel', $channel)
            ->where('enabled', true)
            ->exists();
    }

    /**
     * Whether a notification can be sent right now on this channel.
     * Checks both the plan cap and today's live count in notification_log.
     */
    public function canSendNow(string $channel): bool
    {
        if (! $this->canUseChannel($channel)) {
            return false;
        }

        $dailyLimit = $this->feature($channel, 'daily_limit');

        // null or 0 in the feature means no SMS/unlimited — treat 0 as blocked, null as unlimited
        if ($dailyLimit === null) {
            return true;
        }

        if ($dailyLimit === 0) {
            return false;
        }

        $sentToday = NotificationLog::where('user_id', $this->user->id)
            ->where('channel', $channel)
            ->where('sent', true)
            ->whereDate('created_at', today())
            ->count();

        return $sentToday < $dailyLimit;
    }

    /** Whether the user can track an additional fuel type. */
    public function canTrackFuelType(string $fuelType): bool
    {
        $limit = $this->fuelTypeLimit();

        if ($limit === null) {
            return true;
        }

        $count = $this->trackedFuelTypeCount();

        // Allow if already tracking this type (not adding a new one)
        $alreadyTracking = UserNotificationPreference::where('user_id', $this->user->id)
            ->where('fuel_type', $fuelType)
            ->exists();

        if ($alreadyTracking) {
            return true;
        }

        return $count < $limit;
    }

    /** Maximum fuel types allowed, or null for unlimited. */
    public function fuelTypeLimit(): ?int
    {
        $features = $this->plan->features ?? [];

        return $features['fuel_types']['max'] ?? 1;
    }

    /** Count of distinct fuel types the user has preferences for. */
    public function trackedFuelTypeCount(): int
    {
        return UserNotificationPreference::where('user_id', $this->user->id)
            ->distinct('fuel_type')
            ->count('fuel_type');
    }

    /** Generic boolean feature flag check. */
    public function can(string $feature): bool
    {
        $features = $this->plan->features ?? [];

        return (bool) ($features[$feature] ?? false);
    }

    /** Count of notifications missed today on a channel. */
    public function missedToday(string $channel): int
    {
        return NotificationLog::where('user_id', $this->user->id)
            ->where('channel', $channel)
            ->where('sent', false)
            ->whereDate('created_at', today())
            ->count();
    }

    /** Count of notifications missed this month on a channel. */
    public function missedThisMonth(string $channel): int
    {
        return NotificationLog::where('user_id', $this->user->id)
            ->where('channel', $channel)
            ->where('sent', false)
            ->whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year)
            ->count();
    }

    /** The resolved plan tier name. */
    public function tier(): string
    {
        return $this->plan->name ?? 'free';
    }

    /** User-facing display label for the resolved tier (e.g. basic → "Daily"). */
    public function displayName(): string
    {
        return $this->plan->displayName();
    }
}
