<?php

namespace App\Services;

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

final class PlanFeatures
{
    /** @var string[] */
    private const array CHANNELS = ['email', 'push', 'whatsapp', 'sms'];

    private Plan $plan;

    private function __construct(private readonly User $user)
    {
        $this->plan = Plan::resolveForUser($user);
    }

    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
    {
        $allowed = [];

        foreach (self::CHANNELS 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->plan->{"{$channel}_enabled"};
    }

    /**
     * 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->dailyLimit($channel);

        // null = unlimited; 0 = blocked even though enabled
        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;
        }

        $alreadyTracking = UserNotificationPreference::where('user_id', $this->user->id)
            ->where('fuel_type', $fuelType)
            ->exists();

        if ($alreadyTracking) {
            return true;
        }

        return $this->trackedFuelTypeCount() < $limit;
    }

    /** Maximum fuel types allowed, or null for unlimited. */
    public function fuelTypeLimit(): ?int
    {
        return $this->plan->max_fuel_types;
    }

    /** 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
    {
        return (bool) ($this->plan->{$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;
    }

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

    /** 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();
    }

    /** Per-channel daily limit. Null on email/push (no cap), int on whatsapp/sms. */
    private function dailyLimit(string $channel): ?int
    {
        return match ($channel) {
            'whatsapp' => $this->plan->whatsapp_daily_limit,
            'sms' => $this->plan->sms_daily_limit,
            default => null,
        };
    }
}
