<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\PointLog;
use App\Models\PointRule;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class PointService
{
    public function __construct(private MemberLevelService $levels)
    {
    }

    /**
     * Award points to a user based on an action. Respects cap_per_day.
     * Returns the PointLog or null when capped / disabled.
     */
    public function awardForAction(User $user, string $action, ?string $referenceType = null, ?string $referenceId = null): ?PointLog
    {
        $rule = PointRule::query()
            ->where('tenant_id', $user->tenant_id)
            ->where('action', $action)
            ->where('is_active', true)
            ->first();

        if (!$rule || $rule->points === 0) {
            return null;
        }

        if ($rule->cap_per_day) {
            $todayEarned = PointLog::query()
                ->where('user_id', $user->id)
                ->where('type', 'earn')
                ->where('reason', $action)
                ->whereDate('created_at', today())
                ->sum('amount');
            if ($todayEarned + $rule->points > $rule->cap_per_day) {
                return null;
            }
        }

        return $this->credit($user, $rule->points, $action, $referenceType, $referenceId);
    }

    public function credit(User $user, int $amount, string $reason, ?string $referenceType = null, ?string $referenceId = null): PointLog
    {
        return DB::transaction(function () use ($user, $amount, $reason, $referenceType, $referenceId) {
            $user->refresh();
            $user->points = (int) $user->points + $amount;
            $user->save();

            $log = PointLog::create([
                'tenant_id' => $user->tenant_id,
                'user_id' => $user->id,
                'type' => $amount >= 0 ? 'earn' : 'spend',
                'amount' => $amount,
                'balance_after' => (int) $user->points,
                'reason' => $reason,
                'reference_type' => $referenceType,
                'reference_id' => $referenceId,
            ]);

            $this->levels->recalculate($user);
            return $log;
        });
    }

    public function spend(User $user, int $amount, string $reason): PointLog
    {
        return $this->credit($user, -abs($amount), $reason);
    }

    public function adminAdjust(User $user, int $delta, string $reason): PointLog
    {
        return DB::transaction(function () use ($user, $delta, $reason) {
            $user->refresh();
            $user->points = (int) $user->points + $delta;
            $user->save();

            $log = PointLog::create([
                'tenant_id' => $user->tenant_id,
                'user_id' => $user->id,
                'type' => 'admin_adjust',
                'amount' => $delta,
                'balance_after' => (int) $user->points,
                'reason' => $reason ?: 'admin_adjust',
            ]);
            $this->levels->recalculate($user);
            return $log;
        });
    }

    public function balance(User $user): int
    {
        return (int) $user->points;
    }
}
