<?php

namespace App\Http\Controllers;

use App\Helpers\Horoscope;
use App\Models\AuditLog;
use App\Models\Post;
use App\Models\User;
use App\Models\Video;
use App\Notifications\NewSubscriberNotification;
use App\Notifications\VideoLikedNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth')->except(['channel']);
    }

    // Typeahead search for members (used by the "Share to member" picker)
    public function searchUsers(Request $request)
    {
        $q = trim((string) $request->query('q', ''));
        if (mb_strlen($q) < 1) {
            return response()->json(['users' => []]);
        }

        $users = User::where('id', '!=', Auth::id())
            ->where(function ($w) use ($q) {
                $w->where('name', 'like', "%{$q}%")
                  ->orWhere('username', 'like', "%{$q}%");
            })
            ->orderBy('name')
            ->limit(8)
            ->get(['id', 'name', 'username', 'avatar']);

        return response()->json([
            'users' => $users->map(fn ($u) => [
                'id'      => $u->id,
                'name'    => $u->name,
                'channel' => $u->channel,
                'avatar'  => $u->avatar_url,
            ]),
        ]);
    }

    // Profile page - personal overview for the authenticated user
    public function profile()
    {
        $user = Auth::user();

        return view('user.profile', compact('user'));
    }

    // Update profile
    public function updateProfile(Request $request)
    {
        $authUser = Auth::user();

        // Super admins may edit any user's profile by passing _edit_user_id
        if ($authUser->isSuperAdmin() && $request->filled('_edit_user_id')) {
            $user = User::findOrFail($request->input('_edit_user_id'));
        } else {
            $user = $authUser;
        }

        $request->validate([
            'name'         => 'required|string|max:255',
            'avatar'       => 'nullable|image|mimes:jpg,jpeg,png,webp|max:5120',
            'bio'          => 'nullable|string|max:500',
            'birthday'     => 'nullable|date',
            'location'     => 'nullable|string|max:100',
            'gender'       => 'nullable|in:male,female,prefer_not_to_say',
            'nationality'  => 'nullable|string|size:2',
            'phone_code'   => 'nullable|string|max:20',
            'phone_number' => 'nullable|string|max:30',
            'timezone'     => 'nullable|timezone:all',
            'slink'               => 'nullable|array',
            'slink.*.platform'   => 'required_with:slink|string|max:30',
            'slink.*.value'      => 'required_with:slink|string|max:500',
            'slink.*.visibility' => 'nullable|in:public,registered,subscribers,only_me',
        ]);

        $data = [
            'name'         => $request->name,
            'bio'          => $request->bio,
            'birthday'     => $request->birthday,
            'location'     => $request->location,
            'gender'       => $request->gender ?: null,
            'nationality'  => $request->nationality ?: null,
            'phone_code'   => $request->phone_code ?: null,
            'phone_number' => $request->phone_number ?: null,
            'timezone'     => $request->timezone ?: null,
        ];

        $nas = app(\App\Services\NasSyncService::class);

        if ($request->hasFile('avatar')) {
            // Delete old avatar (handles both flat and new relative-path formats)
            $nas->deleteLocalAvatar($user);

            $ext         = $request->file('avatar')->getClientOriginalExtension() ?: 'webp';
            $profileDir  = $nas->localProfileDir($user);
            $destFilename = "avatar.{$ext}";
            $destPath     = "{$profileDir}/{$destFilename}";
            $relPath      = 'users/' . $nas->userSlug($user) . "/profile/{$destFilename}";

            @mkdir($profileDir, 0755, true);
            $request->file('avatar')->move($profileDir, $destFilename);
            $data['avatar'] = $relPath;
        }

        $user->update($data);

        // Push avatar to NAS and remove local copy when NAS is primary storage
        if ($nas->isEnabled()) {
            if ($request->hasFile('avatar')) {
                $destPath = storage_path('app/' . $data['avatar']);
                if (file_exists($destPath)) {
                    $nas->syncAvatar($user, $destPath);
                    $nas->deleteLocalAvatar($user);
                }
            }
        }

        // Sync social links
        $user->socialLinks()->delete();
        $order = 0;
        foreach ($request->input('slink', []) as $entry) {
            $platform = trim($entry['platform'] ?? '');
            $value    = trim($entry['value'] ?? '');
            if ($platform && $value) {
                $user->socialLinks()->create([
                    'platform'   => $platform,
                    'value'      => $value,
                    'visibility' => $entry['visibility'] ?? 'public',
                    'sort_order' => $order++,
                ]);
            }
        }

        // Redirect back to profile page, or channel settings if admin edited another user
        if ($authUser->isSuperAdmin() && $authUser->id !== $user->id) {
            return redirect()->route('channel', $user->channel)->with('toast_success', 'Profile updated!')->with('_open_tab', 'settings');
        }

        return redirect()->route('profile')->with('toast_success', 'Profile updated!');
    }

    // Settings page - redirects to channel settings tab
    public function settings()
    {
        return redirect()->route('channel')->with('_open_tab', 'settings');
    }

    // Update settings (password)
    public function updateSettings(Request $request)
    {
        $user = Auth::user();

        $request->validate([
            'current_password' => 'required',
            'new_password' => 'required|min:8|confirmed',
        ]);

        if (! Hash::check($request->current_password, $user->password)) {
            return back()->withErrors(['current_password' => 'Current password is incorrect']);
        }

        $user->update([
            'password' => Hash::make($request->new_password),
        ]);

        AuditLog::record('user.password_changed');

        return redirect()->route('channel')->with('toast_success', 'Password updated!')->with('_open_tab', 'settings');
    }

    // Save a single notification preference toggle (AJAX)
    public function updateNotificationPreferences(Request $request)
    {
        $request->validate([
            'key'   => ['required', 'string', 'in:' . implode(',', array_keys(User::notifDefaults()))],
            'value' => ['required', 'boolean'],
        ]);

        $user  = Auth::user();
        $prefs = $user->notification_preferences ?? [];
        $prefs[$request->key] = (bool) $request->value;
        $user->update(['notification_preferences' => $prefs]);

        return response()->json(['ok' => true]);
    }

    // Logout all other devices
    public function logoutAllDevices(Request $request)
    {
        $request->validate(['password' => 'required']);

        if (! Hash::check($request->password, Auth::user()->password)) {
            return back()->withErrors(['logout_password' => 'Incorrect password.'])->with('_open_tab', 'settings');
        }

        Auth::logoutOtherDevices($request->password);

        // AuthenticateSession stores a hash of the password; logoutOtherDevices rehashes it,
        // so we must update the session's copy or the current session gets invalidated too.
        $request->session()->put(
            'password_hash_' . Auth::getDefaultDriver(),
            Auth::user()->getAuthPassword()
        );

        AuditLog::record('user.logout_all');

        return redirect()->route('channel')->with('toast_success', 'All other sessions have been logged out.')->with('_open_tab', 'settings');
    }

    // User's channel page - view videos
    public function channel($username = null)
    {
        if ($username) {
            // Look up by username slug only — never by sequential ID
            $user = User::where('username', $username)->firstOrFail();
        } else {
            $user = Auth::user();
            if (! $user) {
                return redirect()->route('login');
            }
            $user->channel; // triggers auto-generation if missing
        }

        $sort      = request('sort', 'latest');
        $isOwner   = Auth::check() && Auth::user()->id === $user->id;
        $preview   = $isOwner && request()->boolean('preview'); // owner viewing as visitor
        $isOwner   = $isOwner && !$preview;

        $baseQuery = $isOwner
            ? Video::where('user_id', $user->id)
            : Video::public()->where('user_id', $user->id);

        $allQuery = clone $baseQuery;

        switch ($sort) {
            case 'popular':
                $baseQuery->withCount('viewers')->orderByDesc('viewers_count');
                break;
            case 'oldest':
                $baseQuery->oldest();
                break;
            default:
                $baseQuery->latest();
        }

        $videos  = $baseQuery->where('is_shorts', false)->get();
        $shorts  = (clone $allQuery)->where('is_shorts', true)->latest()->get();
        $withFirstVideo = fn($q) => $q->orderBy('playlist_videos.position')->limit(1);
        $playlists = $isOwner
            ? $user->playlists()->with(['videos' => $withFirstVideo])->orderBy('created_at', 'desc')->get()
            : $user->playlists()->public()->where('is_default', false)->with(['videos' => $withFirstVideo])->orderBy('created_at', 'desc')->get();

        $totalViews = \DB::table('video_views')
            ->whereIn('video_id', $user->videos()->pluck('id'))
            ->count();

        // Filter social links by visibility for the current viewer
        $viewer       = Auth::user();
        $isOwner      = $viewer && $viewer->id === $user->id;
        $isSubscriber = $viewer && !$isOwner && $viewer->isSubscribedTo($user);

        $socialLinks = $user->socialLinks()
            ->orderBy('sort_order')
            ->get()
            ->filter(function ($link) use ($isOwner, $viewer, $isSubscriber) {
                if ($isOwner) return true;
                return match ($link->visibility) {
                    'public'      => true,
                    'registered'  => (bool) $viewer,
                    'subscribers' => $viewer && $isSubscriber,
                    'only_me'     => false,
                    default       => false,
                };
            });

        // Posts for the Wall tab
        $posts = Post::where('user_id', $user->id)
            ->with(['user', 'video', 'reactions', 'postImages', 'postVideos.video'])
            ->latest()
            ->get();

        // Horoscope
        $horoscope     = Horoscope::getSign($user->birthday);
        $viewerSign    = null;
        $compatibility = null;
        if ($viewer && ! $isOwner && $viewer->birthday) {
            $viewerSign    = Horoscope::getSign($viewer->birthday);
            $compatibility = Horoscope::compatibility($horoscope, $viewerSign);
        }

        $canEdit = $isOwner || (Auth::check() && Auth::user()->isSuperAdmin());

        return view('user.channel', compact(
            'user', 'videos', 'shorts', 'playlists', 'totalViews', 'sort',
            'socialLinks', 'isSubscriber', 'isOwner', 'canEdit', 'posts', 'horoscope', 'viewerSign', 'compatibility',
            'preview'
        ));
    }

    // Watch history
    public function history()
    {
        $user = Auth::user();

        // Get videos the user has watched, ordered by most recently watched
        // Include private videos since they are the user's own
        $videoIds = \DB::table('video_views')
            ->where('user_id', $user->id)
            ->orderBy('watched_at', 'desc')
            ->pluck('video_id')
            ->unique();

        $videos = Video::whereIn('id', $videoIds)
            ->where(function ($q) use ($user) {
                $q->where('visibility', '!=', 'private')
                    ->orWhere('user_id', $user->id);
            })
            ->get()
            ->sortBy(function ($video) use ($videoIds) {
                return $videoIds->search($video->id);
            });

        return view('user.history', compact('videos'));
    }

    // Clear watch history
    public function clearHistory()
    {
        \DB::table('video_views')
            ->where('user_id', Auth::id())
            ->delete();

        return redirect()->route('history')->with('toast_success', 'Watch history cleared.');
    }

    // Liked videos
    public function liked()
    {
        $user = Auth::user();
        // Include private videos in liked (user's own private videos)
        $videos = $user->likes()
            ->where(function ($q) use ($user) {
                $q->where('visibility', '!=', 'private')
                    ->orWhere('videos.user_id', $user->id);
            })
            ->latest()
            ->paginate(12);

        return view('user.liked', compact('videos'));
    }

    // Like a video
    public function like(Video $video)
    {
        $user = Auth::user();

        if (! $video->isLikedBy($user)) {
            $video->likes()->attach($user->id);
        }

        return back();
    }

    // Unlike a video
    public function unlike(Video $video)
    {
        $user = Auth::user();

        $video->likes()->detach($user->id);

        return back();
    }

    // Toggle like (API)
    public function toggleLike(Video $video)
    {
        $user = Auth::user();

        if ($video->isLikedBy($user)) {
            $video->likes()->detach($user->id);
            $liked = false;
        } else {
            $video->likes()->attach($user->id);
            $liked = true;
            if ($video->user_id && $video->user_id !== $user->id) {
                try { $video->user->notify(new VideoLikedNotification($video, $user)); } catch (\Throwable) {}
            }
        }

        return response()->json([
            'liked' => $liked,
            'like_count' => $video->like_count,
        ]);
    }

    public function recordProfileVisit(Request $request, User $user)
    {
        // Don't record self-visits or repeated visits from the same person in the last 30 minutes
        $visitorId = Auth::id();
        $deviceId  = $request->cookie('_did');

        if ($visitorId && $visitorId === $user->id) {
            return response()->json(['ok' => true, 'skipped' => 'self']);
        }

        $sourceVideoId = $request->integer('source_video_id') ?: null;
        if ($sourceVideoId && ! Video::whereKey($sourceVideoId)->exists()) {
            $sourceVideoId = null;
        }

        $dedup = \App\Models\ProfileVisit::where('profile_user_id', $user->id)
            ->where('created_at', '>=', now()->subMinutes(30))
            ->when($visitorId, fn ($q) => $q->where('visitor_user_id', $visitorId))
            ->when(! $visitorId && $deviceId, fn ($q) => $q->whereNull('visitor_user_id')->where('device_id', $deviceId))
            ->when($sourceVideoId, fn ($q) => $q->where('source_video_id', $sourceVideoId))
            ->exists();

        if ($dedup) {
            return response()->json(['ok' => true, 'skipped' => 'dedup']);
        }

        $ip  = $request->header('CF-Connecting-IP') ?? $request->header('X-Real-IP') ?? $request->ip();
        $geo = \App\Services\GeoIpService::lookup($ip);

        \App\Models\ProfileVisit::create([
            'profile_user_id' => $user->id,
            'visitor_user_id' => $visitorId,
            'device_id'       => $deviceId,
            'source_video_id' => $sourceVideoId,
            'ip_address'      => $ip,
            'country'         => $geo['country'] ?? null,
            'created_at'      => now(),
        ]);

        return response()->json(['ok' => true]);
    }

    public function toggleSubscribe(Request $request, User $user)
    {
        $me = Auth::user();

        if ($me->id === $user->id) {
            return response()->json(['error' => 'Cannot subscribe to yourself'], 422);
        }

        if ($me->isSubscribedTo($user)) {
            $me->subscriptions()->detach($user->id);
            $subscribed = false;
        } else {
            $sourceVideoId = $request->integer('source_video_id') ?: null;
            if ($sourceVideoId && ! \App\Models\Video::whereKey($sourceVideoId)->exists()) {
                $sourceVideoId = null;
            }
            $me->subscriptions()->attach($user->id, ['source_video_id' => $sourceVideoId]);
            $subscribed = true;
            try { $user->notify(new NewSubscriberNotification($me)); } catch (\Throwable) {}
        }

        return response()->json([
            'subscribed'       => $subscribed,
            'subscriber_count' => $user->fresh()->subscriber_count,
        ]);
    }

    public function notificationCount()
    {
        return response()->json(['unread_count' => Auth::user()->unreadNotifications()->count()]);
    }

    public function fetchNotifications()
    {
        $user = Auth::user();

        $rawNotifications = $user->notifications()->latest()->take(50)->get();

        // Bulk-fetch video state only for video-linked notifications
        $videoIds = $rawNotifications
            ->pluck('data.video_id')
            ->filter()
            ->unique()
            ->values();

        $videos = $videoIds->isNotEmpty()
            ? \App\Models\Video::whereIn('id', $videoIds)
                ->whereIn('visibility', ['public', 'unlisted'])
                ->get(['id', 'thumbnail', 'visibility'])
                ->keyBy('id')
            : collect();

        $notifications = $rawNotifications
            ->filter(function ($n) use ($videos) {
                $videoId = $n->data['video_id'] ?? null;
                // Non-video notifications (subscriber, like, post, new_user) always pass
                if (!$videoId) return true;
                // Video notifications only if the video is still visible
                return $videos->has($videoId);
            })
            ->take(30)
            ->map(function ($n) use ($videos) {
                $data = $n->data;
                if (!empty($data['video_id'])) {
                    $data['video_thumbnail'] = $videos->get($data['video_id'])?->thumbnail ?? null;
                }
                return [
                    'id'   => $n->id,
                    'read' => ! is_null($n->read_at),
                    'time' => $n->created_at->diffForHumans(),
                    'data' => $data,
                ];
            })
            ->values();

        return response()->json([
            'notifications' => $notifications,
            'unread_count'  => $user->unreadNotifications()->count(),
        ]);
    }

    public function markNotificationRead(string $id)
    {
        $notification = Auth::user()->notifications()->findOrFail($id);
        $notification->markAsRead();
        return response()->json(['ok' => true]);
    }

    public function markAllNotificationsRead()
    {
        Auth::user()->unreadNotifications->markAsRead();
        return response()->json(['ok' => true]);
    }

    public function updateAvatar(Request $request)
    {
        $request->validate(['path' => 'required|string|max:300']);
        $user     = Auth::user();
        $filename = basename($request->path);
        $tempPath = storage_path('app/public/avatars/' . $filename);

        $nas = app(\App\Services\NasSyncService::class);

        // Move temp file into the user's profile directory
        $profileDir  = $nas->localProfileDir($user);
        $ext         = pathinfo($filename, PATHINFO_EXTENSION) ?: 'webp';
        $destFilename = "avatar.{$ext}";
        $destPath     = "{$profileDir}/{$destFilename}";
        $relPath      = 'users/' . $nas->userSlug($user) . "/profile/{$destFilename}";

        // Delete old avatar before moving new one in (handles both path formats)
        $nas->deleteLocalAvatar($user);

        @mkdir($profileDir, 0755, true);
        if (file_exists($tempPath)) {
            rename($tempPath, $destPath);
        }

        $user->update(['avatar' => $relPath]);

        if ($nas->isEnabled() && file_exists($destPath)) {
            $nas->syncAvatar($user, $destPath);
            $nas->deleteLocalAvatar($user);
        }

        return response()->json(['ok' => true]);
    }

    public function updateBanner(Request $request)
    {
        $request->validate(['path' => 'required|string|max:300']);
        $user     = Auth::user();
        $filename = basename($request->path);
        $tempPath = storage_path('app/public/banners/' . $filename);

        $nas = app(\App\Services\NasSyncService::class);

        // Move temp file into the user's profile directory
        $profileDir   = $nas->localProfileDir($user);
        $ext          = pathinfo($filename, PATHINFO_EXTENSION) ?: 'webp';
        $destFilename = "cover.{$ext}";
        $destPath     = "{$profileDir}/{$destFilename}";
        $relPath      = 'users/' . $nas->userSlug($user) . "/profile/{$destFilename}";

        // Delete old banner before moving new one in (handles both path formats)
        $nas->deleteLocalBanner($user);

        @mkdir($profileDir, 0755, true);
        if (file_exists($tempPath)) {
            rename($tempPath, $destPath);
        }

        $user->update(['banner' => $relPath]);

        if ($nas->isEnabled() && file_exists($destPath)) {
            $nas->syncCover($user, $destPath);
            $nas->deleteLocalBanner($user);
        }

        return response()->json(['ok' => true]);
    }
}
