<?php

namespace App\Services;

use App\Models\User;
use App\Notifications\UserFollowedNotification;
use App\Events\Achievements\AchievementCheckRequested;
use Illuminate\Support\Facades\DB;

/**
 * FollowService
 *
 * Manages follow / unfollow operations on the user_followers table.
 * Convention:
 *   follower_id  = the user doing the following
 *   user_id      = the user being followed
 *
 * Counters in user_statistics are kept in sync atomically inside a transaction.
 */
final class FollowService
{
    public function __construct(private readonly XPService $xp) {}

    /**
     * Follow $targetId on behalf of $actorId.
     *
     * @return bool  true if a new follow was created, false if already following
     *
     * @throws \InvalidArgumentException if self-follow attempted
     */
    public function follow(int $actorId, int $targetId): bool
    {
        if ($actorId === $targetId) {
            throw new \InvalidArgumentException('Cannot follow yourself.');
        }

        $inserted = false;

        DB::transaction(function () use ($actorId, $targetId, &$inserted) {
            $rows = DB::table('user_followers')->insertOrIgnore([
                'user_id'     => $targetId,
                'follower_id' => $actorId,
                'created_at'  => now(),
            ]);

            if ($rows === 0) {
                // Already following – nothing to do
                return;
            }

            $inserted = true;

            // Increment following_count for actor, followers_count for target
            $this->incrementCounter($actorId, 'following_count');
            $this->incrementCounter($targetId, 'followers_count');
        });

        // Record activity event outside the transaction to avoid deadlocks
        if ($inserted) {
            try {
                \App\Models\ActivityEvent::record(
                    actorId: $actorId,
                    type: \App\Models\ActivityEvent::TYPE_FOLLOW,
                    targetType: \App\Models\ActivityEvent::TARGET_USER,
                    targetId: $targetId,
                );
            } catch (\Throwable) {}

            $targetUser = User::query()->find($targetId);
            $actorUser = User::query()->find($actorId);
            if ($targetUser && $actorUser) {
                $targetUser->notify(new UserFollowedNotification($actorUser));
            }

            $this->xp->awardFollowerReceived($targetId, $actorId);
            event(new AchievementCheckRequested($targetId));
        }

        return $inserted;
    }

    /**
     * Unfollow $targetId on behalf of $actorId.
     *
     * @return bool  true if a follow row was removed, false if wasn't following
     */
    public function unfollow(int $actorId, int $targetId): bool
    {
        if ($actorId === $targetId) {
            return false;
        }

        $deleted = false;

        DB::transaction(function () use ($actorId, $targetId, &$deleted) {
            $rows = DB::table('user_followers')
                ->where('user_id', $targetId)
                ->where('follower_id', $actorId)
                ->delete();

            if ($rows === 0) {
                return;
            }

            $deleted = true;

            $this->decrementCounter($actorId, 'following_count');
            $this->decrementCounter($targetId, 'followers_count');
        });

        return $deleted;
    }

    /**
     * Toggle follow state. Returns the new following state.
     */
    public function toggle(int $actorId, int $targetId): bool
    {
        if ($this->isFollowing($actorId, $targetId)) {
            $this->unfollow($actorId, $targetId);
            return false;
        }

        $this->follow($actorId, $targetId);
        return true;
    }

    public function isFollowing(int $actorId, int $targetId): bool
    {
        return DB::table('user_followers')
            ->where('user_id', $targetId)
            ->where('follower_id', $actorId)
            ->exists();
    }

    /**
     * Current followers_count for a user (from cached column, not live count).
     */
    public function followersCount(int $userId): int
    {
        return (int) DB::table('user_statistics')
            ->where('user_id', $userId)
            ->value('followers_count');
    }

    // ─── Private helpers ─────────────────────────────────────────────────────

    private function incrementCounter(int $userId, string $column): void
    {
        DB::table('user_statistics')->updateOrInsert(
            ['user_id' => $userId],
            [
                $column      => DB::raw("COALESCE({$column}, 0) + 1"),
                'updated_at' => now(),
                'created_at' => now(),          // ignored on update
            ]
        );
    }

    private function decrementCounter(int $userId, string $column): void
    {
        DB::table('user_statistics')
            ->where('user_id', $userId)
            ->where($column, '>', 0)
            ->update([
                $column      => DB::raw("{$column} - 1"),
                'updated_at' => now(),
            ]);
    }
}
