<?php

namespace App\Providers;

use App\Filament\Resources\Streams\StreamResource;
use App\Filament\Resources\UserMessages\UserMessageResource;
use App\Filament\Resources\UserReports\UserReportResource;
use App\Filament\Resources\Users\UserResource;
use App\Model\GlobalAnnouncement;
use App\Model\PublicPage;
use App\Model\UserReport;
use App\Model\Wallet;
use GrahamCampbell\Markdown\Facades\Markdown;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use Jenssegers\Agent\Agent;
use Mews\Purifier\Facades\Purifier;
use Pusher\Pusher;
use Pusher\PusherException;
use Ramsey\Uuid\Uuid;
use Cookie;

class GenericHelperServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Check if user meets all ID verification steps.
     *
     * @return bool
     */
    public static function isUserVerified()
    {
        if (
            (Auth::user()->verification && Auth::user()->verification->status == 'verified') &&
            Auth::user()->birthdate &&
            Auth::user()->email_verified_at
        ) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param $contactUserID - Contacted users
     * @param $userID - User sending the message
     * @return bool
     */
    public static function hasUserBlocked(int $blockerId, int $blockedId): bool
    {
        // Does blockerId's "blocked" list contain blockedId?
        return DB::table('user_list_members as ulm')
            ->where('ulm.user_id', $blockedId)
            ->whereExists(function ($q) use ($blockerId) {
                $q->select(DB::raw(1))
                    ->from('user_lists as l')
                    ->whereColumn('l.id', 'ulm.list_id')
                    ->where('l.user_id', $blockerId)
                    ->where('l.type', 'blocked');
            })
            ->exists();
    }

    /**
     * Creates a default wallet for a user.
     * @param $user
     */
    public static function createUserWallet($user)
    {
        try {
            $userWallet = Wallet::query()->where('user_id', $user->id)->first();
            if ($userWallet == null) {
                // generate unique id for wallet
                do {
                    $id = Uuid::uuid4()->getHex();
                } while (Wallet::query()->where('id', $id)->first() != null);

                $balance = 0.0;
                if(getSetting('profiles.default_wallet_balance_on_register') && getSetting('profiles.default_wallet_balance_on_register') != 0){
                    $balance = getSetting('profiles.default_wallet_balance_on_register');
                }
                Wallet::create([
                    'id' => $id,
                    'user_id' => $user->id,
                    'total' => $balance,
                ]);
            }
        } catch (\Exception $exception) {
            Log::error('User wallet creation error: '.$exception->getMessage());
        }
    }

    /**
     * Static function that handles remote storage drivers.
     *
     * @param $value
     * @return string
     */
    public static function getStorageAvatarPath($value) {

        if($value && $value !== '/img/default-avatar.jpg'){
            return self::getFilePathByActiveStorageDriver($value);
        }else{
            return str_replace('storage/', '', asset('/img/default-avatar.jpg'));
        }
    }

    public static function getFilePathByActiveStorageDriver($value): string {
        if (getSetting('storage.driver') == 's3') {
            if (getSetting('storage.aws_cdn_enabled') && getSetting('storage.aws_cdn_presigned_urls_enabled')) {
                $fileUrl = AttachmentServiceProvider::signAPrivateDistributionPolicy(
                    'https://'.getSetting('storage.cdn_domain_name').'/'.$value
                );
            } elseif (getSetting('storage.aws_cdn_enabled')) {
                $fileUrl = 'https://'.getSetting('storage.cdn_domain_name').'/'.$value;
            } else {
                $fileUrl = 'https://'.getSetting('storage.aws_bucket_name').'.s3.'.getSetting('storage.aws_region').'.amazonaws.com/'.$value;
            }
            return $fileUrl;
        }
        elseif(getSetting('storage.driver') == 'wasabi' || getSetting('storage.driver') == 'do_spaces' || getSetting('storage.driver') == 'r2'){
            return Storage::url($value);
        }
        elseif(getSetting('storage.driver') == 'minio'){
            return rtrim(getSetting('storage.minio_endpoint'), '/').'/'.getSetting('storage.minio_bucket_name').'/'.$value;
        }
        elseif(getSetting('storage.driver') == 'pushr'){
            return rtrim(getSetting('storage.pushr_cdn_hostname'), '/').'/'.$value;
        }
        else{
            return Storage::disk('public')->url($value);
        }
    }

    /**
     * Static function that handles remote storage drivers.
     *
     * @param $value
     * @return string
     */
    public static function getStorageCoverPath($value) {
        if($value){
            return self::getFilePathByActiveStorageDriver($value);
        }else{
            return asset('/img/default-cover.png');
        }
    }

    /**
     * Helper to detect mobile usage.
     * @return bool
     */
    public static function isMobileDevice() {
        $agent = new Agent();
        return $agent->isMobile();
    }

    /**
     * Returns true if email enforce is not enabled or if is set to true and user is verified.
     * @return bool
     */
    public static function isEmailEnforcedAndValidated() {
        return (Auth::check() && Auth::user()->email_verified_at) || (Auth::check() && !getSetting('site.enforce_email_validation'));
    }

    public static function parseProfileMarkdownBio($bio) {
        if(getSetting('profiles.allow_profile_bio_markdown')){
            $parsedOutput = Purifier::clean(Markdown::convert($bio)->getContent());
            return $parsedOutput;
        }
        return $bio;
    }

    public static function parseSafeHTML($text) {
        return  Purifier::clean((str_replace("\n", "<br>", strip_tags($text))));
    }

    /**
     * Fetches list of all public pages to be show in footer.
     * @return mixed
     */
    public static function getFooterPublicPages() {
        $pages = [];
        if (InstallerServiceProvider::checkIfInstalled()) {
            $pages = PublicPage::where('shown_in_footer', 1)->orderBy('page_order')->get();
        }
        return $pages;
    }

    /**
     * Get Privacy page.
     * @return mixed
     */
    public static function getPrivacyPage() {
        try{
            return PublicPage::where('is_privacy', 1)->first();
        }
        catch (\Exception $e){
            return PublicPage::first();
        }
    }

    /**
     * Get TOS page.
     * @return mixed
     */
    public static function getTOSPage() {
        try{
            return PublicPage::where('is_tos', 1)->first();
        }
        catch (\Exception $e){
            return PublicPage::first();
        }
    }

    /*
    * Get Privacy page.
    * @return mixed
    */
    public static function getModelAgreementPage() {
        try{
            return PublicPage::where('slug', 'creator-agreement')->first();
        }
        catch (\Exception $e){
            return null;
        }
    }

    /**
     * Verifies if admin added a minimum posts limit for creators to earn money.
     * @param $user
     * @return bool
     */
    public static function creatorCanEarnMoney($user) {
        if(intval(getSetting("compliance.minimum_posts_until_creator")) > 0 && count($user->posts) < intval(getSetting('compliance.minimum_posts_until_creator'))){
            return false;
        }
        if(getSetting('compliance.monthly_posts_before_inactive') && !$user->is_active_creator){
            return false;
        }
        return true;
    }

    /**
     * Returns the preferred user local
     * TODO: This is only used in the payments module | Maybe delete it and use LocaleProvider based alternative.
     * @return \Illuminate\Config\Repository|mixed|null
     */
    public static function getPreferredLanguage() {
        // Defaults
        if (!Session::has('locale')) {
            if (InstallerServiceProvider::checkIfInstalled()) {
                return getSetting('site.default_site_language');
            } else {
                return Config::get('app.locale');
            }
        }
        // If user has locale setting, use that one
        if (isset(Auth::user()->settings['locale'])) {
            return Auth::user()->settings['locale'];
        }
        return getSetting('site.default_site_language');
    }

    /**
     * Fetches the default OGMeta image to be used (except for profile).
     * @return \Illuminate\Config\Repository|mixed|string|null
     */
    public static function getOGMetaImage() {
        if(getSetting('site.default_og_image')){
            return getSetting('site.default_og_image');
        }
        return asset('img/logo-black.png');
    }

    /**
     * Gets site direction. If rtl cookie not set, defaults to site setting.
     * @return \Illuminate\Config\Repository|mixed|null
     */
    public static function getSiteDirection() {
        if(is_null(Cookie::get('app_rtl'))){
            return getSetting('site.default_site_direction');
        }
        return Cookie::get('app_rtl');
    }

    public static function getSiteTheme() {
        $mode = Cookie::get('app_theme');
        if(!$mode){
            $mode = getSetting('site.default_user_theme');
        }
        return $mode;
    }

    public static function getLatestGlobalMessage() {
        try {
            if (!Schema::hasTable('global_announcements')) {
                return null;
            }

            $messages = GlobalAnnouncement::all();
            $skippedIDs = [];

            foreach($messages as $message){
                if(request()->cookie('dismissed_banner_'.$message->id)){
                    $skippedIDs[] = $message->id;
                }
            }

            $userIsVerified = Auth::check() && self::isUserVerified();

            $message = GlobalAnnouncement::orderBy('created_at', 'desc')
                ->where('is_published', 1)
                ->when(!$userIsVerified, fn ($q) => $q->where('id_verified_only', false))
                ->whereNotIn('id', $skippedIDs)
                ->first();

            return $message;
        } catch (\Throwable $e) {
            return null;
        }
    }

    public static function isUserOnline($userId, string $channelName = 'presence-global'): bool
    {
        $conn = config('broadcasting.connections.pusher');   // already swapped to soketi when needed
        $key = data_get($conn, 'key');
        $secret = data_get($conn, 'secret');
        $appId = data_get($conn, 'app_id');
        $opts = (array) data_get($conn, 'options', []);

        // Guard: only require key/secret/app_id (cluster is NOT required for Soketi)
        if (!$key || !$secret || !$appId) {
            return false;
        }

        // Build client options for either backend
        $clientOptions = [];
        foreach (['cluster', 'host', 'port', 'scheme', 'timeout'] as $k) {
            if (array_key_exists($k, $opts)) {
                $clientOptions[$k] = $opts[$k];
            }
        }
        if (isset($clientOptions['port'])) {
            $clientOptions['port'] = (int) $clientOptions['port']; // SDK expects int
        }

        // Prefer explicit useTLS; otherwise derive from scheme
        $clientOptions['useTLS'] = array_key_exists('useTLS', $opts)
            ? (bool) $opts['useTLS']
            : (($opts['scheme'] ?? 'http') === 'https');

        // Pusher PHP SDK (same for Soketi)
        try {
            $pusher = new Pusher($key, $secret, $appId, $clientOptions);
        } catch (PusherException $e) {
            Log::warning('Presence lookup failed', ['error' => $e->getMessage()]);
        }

        // Ensure proper presence prefix
        if (strpos($channelName, 'presence-') !== 0) {
            $channelName = 'presence-'.ltrim($channelName, '-');
        }

        try {
            $resp = $pusher->get('/channels/'.$channelName.'/users');
            foreach ((array) ($resp->users ?? []) as $member) {
                if ((string) ($member->id ?? '') === (string) $userId) {
                    return true;
                }
            }
        } catch (\Throwable $e) {
            Log::warning('Presence lookup failed', ['error' => $e->getMessage()]);
        }

        return false;
    }

    public static function getReportLinks(UserReport $report): array
    {
        try {
            if ($report->stream_id && $report->reportedStream) {
                return [
                    'admin' => StreamResource::getUrl('edit', ['record' => $report->stream_id]),
                    'public' => route('public.stream.get', [
                        'streamID' => $report->reportedStream->id,
                        'slug' => $report->reportedStream->slug,
                    ]),
                ];
            }

            if ($report->message_id) {
                return [
                    'admin' => UserMessageResource::getUrl('edit', ['record' => $report->message_id]),
                    'public' => null,
                ];
            }

            if ($report->post_id && $report->reportedUser) {
                return [
                    'admin' => UserReportResource::getUrl('edit', ['record' => $report->post_id]),
                    'public' => route('posts.get', [
                        'post_id' => $report->post_id,
                        'username' => $report->reportedUser->username,
                    ]),
                ];
            }

            if ($report->reporterUser && $report->reportedUser) {
                return [
                    'admin' => UserResource::getUrl('edit', ['record' => $report->user_id]),
                    'public' => route('profile', ['username' => $report->reportedUser->username]),
                ];
            }
        } catch (\Throwable $e) {
            // Optionally log the error
        }

        return [
            'admin' => null,
            'public' => null,
        ];
    }

    public static function resolveReportType(UserReport $report): string
    {
        if ($report->stream_id) {
            return 'Stream';
        }

        if ($report->message_id) {
            return 'Message';
        }

        if ($report->post_id) {
            return 'Post';
        }

        return 'User';
    }

    public static function getTotalLikesForUser($userID): int {
        $count = DB::table('reactions as r')
            ->join('posts as p', 'p.id', '=', 'r.post_id')
            ->where('p.user_id', $userID)
            ->count();
        return $count;
    }
}
