<?php

namespace App\Http\Controllers\Api\Platform;

use App\Exceptions\EmailAlreadyExistException;
use App\Exceptions\InvalidAccountBlockingAttempt;
use App\Exceptions\InvalidFollowException;
use App\Exceptions\InvalidPasswordException;
use App\Exceptions\PasswordAlreadyUsedException;
use App\Exceptions\PasswordConfirmationException;
use App\Exceptions\TelephoneAlreadyExistException;
use App\Exceptions\UploadFileException;
use App\Exceptions\UserNameAlreadyExistException;
use App\Exceptions\WalletCurrencyChangeException;
use App\Http\Controllers\Api\Auth\AuthenticateController;
use App\Http\Controllers\Controller;
use App\Models\BlockedAccounts;
use App\Models\Business;
use App\Models\Follower;
use App\Models\Like;
use App\Models\Post;
use App\Models\PostHashTag;
use App\Models\ProductHashTag;
use App\Models\Save;
use App\Models\Share;
use App\Models\User;
use App\RequestRules\Api\UserRules;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class UserController extends Controller
{
    /**
     * Generate random and unique user name
     *
     * @param Request $request
     * @return Response|Application|ResponseFactory
     */
    public function uniqueUsernames(Request $request): Response|Application|ResponseFactory
    {
        $base = $request->{'base'};

        return api_response(100, "Okay", generate_user_name(
            $base, 6
        ));
    }

    /**
     * Create user interests
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     * @throws Exception
     */
    public function saveInterest(Request $request): Response|Application|ResponseFactory
    {
        $result = User::createUserInterest($request->get('interests') ?? []);

        if ($result == null) {
            return api_response(120, __("error.unknown_error"),);
        }

        return api_response(100, "Okay");
    }

    /**
     * Configure account
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function configAccount(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::accountConfig());
        $data = $request->all();

        /** @var User $user */
        $user = auth()->user();
        try {
            //Get user wallet
            $wallet = $user->{"wallet"};
            $result = User::configureAccount($data, $wallet, $user);
            return api_response(100, "Okay", $result);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::configAccount');
            $userExceptions = self::returnUserExceptions($exception);
            if ($userExceptions != null) {
                return $userExceptions;
            }
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Get most used user or business hashtags
     *
     * @return Response|Application|ResponseFactory
     */
    public function mostUsedHashTag(): Response|Application|ResponseFactory
    {
        $tags = ProductHashTag::mostUsedHashTags();
        $tags = array(
            ...$tags,
            ...PostHashTag::mostUsedHashTags()
        );
        $tags = array_unique($tags);

        return api_response(100, "Okay", array_values($tags));
    }

    /**
     * Get user profile
     * @return Application|ResponseFactory|Response
     */
    public function profile(): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = auth()->user();

        try {
            $allData = array(
                "user" => AuthenticateController::responseWithUser($user),
                "posts" => Post::getUsersPost($user, 1),
                "saves" => Save::userSavedPosts($user, 1),
                "shares" => Share::userSharedPosts($user, 1),
                "statistics" => $user->computeProfileStatistics(),
            );
            return api_response(100, "Okay", $allData);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::profile');
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Get user's posts
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function getUsersPosts(Request $request): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = auth()->user();

        try {
            $posts = Post::getUsersPost($user, $request->get("page"));

            return api_response(100, "Okay", $posts);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::getUsersPost');
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Get user's favorite posts
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function getUsersPostsFavorite(Request $request): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = auth()->user();

        try {
            $posts = Save::userSavedPosts($user, $request->get("page"));

            return api_response(100, "Okay", $posts);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::getUsersPost');
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Get user's share posts
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function getUsersSharePosts(Request $request): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = auth()->user();

        try {
            $posts = Share::userSharedPosts($user, $request->get("page"));

            return api_response(100, "Okay", $posts);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::getUsersSharePosts');
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Edit profile
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function editProfile(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::editProfile());
        $data = $request->all();

        /** @var User $user */
        $user = auth()->user();

        try {
            $result = $user->editProfile($data);

            return api_response(100, "Okay", $result);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::editProfile');

            $userExceptions = self::returnUserExceptions($exception);
            if ($userExceptions != null) {
                return $userExceptions;
            }
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Follow or unfollow another user
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function followUser(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::followUser());

        try {
            Follower::followUser($request->get("following_user_id"));

            return api_response(100, "Okay");
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::followUser');
            $userExceptions = self::returnUserExceptions($exception);
            if ($userExceptions != null) {
                return $userExceptions;
            }
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Get followers
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function getFollowers(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = auth()->user();

            $followers = $user->getFollowers($request->{"page"});

            $finalData = default_paginator_format(
                $followers->lastPage(),
                $followers->total(),
                $followers->currentPage(),
                'followers',
                Follower::serializeFollowers($followers->items()),
            );

            return api_response(100, "Okay", $finalData);
        } catch (Exception $exception) {
            log_debug($exception, "UserController::getFollowers");
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Search followers
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function searchFollowers(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = auth()->user();

            $followers = Follower::searchFollowers(
                $user,
                $request->get('query') ?? "",
                $request->get('page')
            );

            $finalData = default_paginator_format(
                $followers->lastPage(),
                $followers->total(),
                $followers->currentPage(),
                'followers',
                Follower::serializeFollowers(
                    $followers->items()
                ),
            );

            return api_response(100, 'Ok', $finalData);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::searchFollowers');
        }

        return api_response(101, __('errors.unknown_error'));
    }

    /**
     * Get followings
     *
     * @param Request $request
     * @return Response|Application|ResponseFactory
     */
    public function getFollowings(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = auth()->user();

            $followings = $user->getFollowings($request->{"page"});

            $finalData = default_paginator_format(
                $followings->lastPage(),
                $followings->total(),
                $followings->currentPage(),
                'followings',
                Follower::serializeFollowings(
                    $followings->items()
                ),
            );

            return api_response(100, "Okay", $finalData);
        } catch (Exception $exception) {
            log_debug($exception, "UserController::getFollowing");
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Search followings
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function searchFollowings(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = auth()->user();

            $followings = Follower::searchFollowings(
                $user,
                $request->get('query') ?? "",
                $request->get('page')
            );

            $finalData = default_paginator_format(
                $followings->lastPage(),
                $followings->total(),
                $followings->currentPage(),
                "followings",
                Follower::serializeFollowings(
                    $followings->items()
                ),
            );

            return api_response(100, 'Ok', $finalData);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::searchFollowings');
        }

        return api_response(101, __('errors.unknown_error'));
    }

    /**
     * Get user followers
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function userFollowers(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = User::findById($request->{"userId"});

            if($user === null) {
                return api_response(120, __('errors.invalid_user'));
            }

            // TODO Apply privacy settings
            $followers = $user->getFollowers($request->{"page"});

            $finalData = default_paginator_format(
                $followers->lastPage(),
                $followers->total(),
                $followers->currentPage(),
                'followers',
                Follower::serializeFollowers($followers->items()),
            );

            return api_response(100, "Okay", $finalData);
        } catch (Exception $exception) {
            log_debug($exception, "UserController::userFollowers");
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Get user followings
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function userFollowings(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = User::findById($request->{"userId"});

            if($user === null) {
                return api_response(120, __('errors.invalid_user'));
            }

            // TODO Apply privacy settings
            $followings = $user->getFollowings($request->{"page"});

            $finalData = default_paginator_format(
                $followings->lastPage(),
                $followings->total(),
                $followings->currentPage(),
                'followings',
                Follower::serializeFollowings(
                    $followings->items()
                ),
            );

            return api_response(100, "Okay", $finalData);
        } catch (Exception $exception) {
            log_debug($exception, "UserController::userFollowings");
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Get business product reviews
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function userReviews(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = User::findById($request->{"userId"});
            if($user === null) {
                return api_response(120, __('errors.invalid_user'));
            }

            /** @var Business $business */
            $business = $user->{'business'};
            if($business === null) {
                return api_response(120, __('errors.user_has_no_business'));
            }

            // TODO Apply privacy settings
            $productRatings = $business->reviews($request->{"page"});

            return api_response(100, 'Ok', $productRatings);
        } catch (Exception $exception) {
            log_debug($exception, "UserController::userReviews");
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * User visitor suggestions
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function visitorSuggestions(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = User::findById($request->{"userId"});

            if($user === null) {
                return api_response(120, __('errors.invalid_user'));
            }

            $suggestions = $user->userSuggestions($request->{"page"});

            return api_response(100, 'Ok', $suggestions);
        }catch (Exception $exception) {
            log_debug($exception, "UserController::userSuggestions");
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Delete follower
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function deleteFollower(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::deleteFollower());

        try {
            Follower::deleteFollower($request->{"follower_user_id"});

            return api_response(100, "Okay");
        } catch (Exception $exception) {
            log_debug($exception, "UserController::deleteFollower");
        }

        return api_response(101, __('errors.unknown_error'));
    }

    /**
     * Visitor profile
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function visitorProfile(Request $request): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = User::findById($request->{"userId"});

        if($user === null) {
            return api_response(120, __('errors.invalid_user'));
        }

        return api_response(100, "Okay", $user->computeProfileForVisitor());
    }

    /**
     * Get request user's post
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function visitorProfilePost(Request $request): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = User::findById($request->{"userId"});

        if($user === null) {
            return api_response(120, __('errors.invalid_user'));
        }

        $userPosts = Post::userMostRecentPost($user, $request->get("page"));

        return api_response(100, "Okay", $userPosts);
    }

    /**
     * Get request user's saved posts
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function visitorProfileSavedPosts(Request $request): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = User::findById($request->{"userId"});

        if($user === null) {
            return api_response(120, __('errors.invalid_user'));
        }

        $savedPosts = Save::userSavedPosts($user, $request->get("page"));

        return api_response(100, "Okay", $savedPosts);
    }

    /**
     * Get request user shared post
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function visitorProfileSharedPosts(Request $request): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = User::findById($request->{"userId"});

        if($user === null) {
            return api_response(120, __('errors.invalid_user'));
        }

        $savedPosts = Share::userSharedPosts($user, $request->get("page"));

        return api_response(100, "Okay", $savedPosts);
    }

    /**
     * Get request user liked post
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function visitorProfileLikedPosts(Request $request): Response|Application|ResponseFactory
    {
        /** @var User $user */
        $user = User::findById($request->{"userId"});

        if($user === null) {
            return api_response(120, __('errors.invalid_user'));
        }

        $userLikedPosts = Like::userLikedPosts($user, $request->get("page"));

        return api_response(100, "Okay", $userLikedPosts);
    }

    /**
     * Change user telephone
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function changePrimaryPhoneNumber(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::changePhoneNumber());
        $data = $request->all();
        try {
            $changedNumber = User::changePrimaryPhoneNumber($data);
            return api_response(100, "Okay", $changedNumber);
        } catch (Exception $exception) {
            log_debug($exception, "UserController::changePrimaryPhoneNumber");
            $userExceptions = self::returnUserExceptions($exception);
            if ($userExceptions != null) {
                return $userExceptions;
            }
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Change user primary email
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function changePrimaryEmail(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::changeEmail());
        $data = $request->all();
        try {
            $changedNumber = User::changePrimaryEmail($data);
            return api_response(100, "Okay", $changedNumber);
        } catch (Exception $exception) {
            log_debug($exception, "UserController::changePrimaryEmail");
            $userExceptions = self::returnUserExceptions($exception);
            if ($userExceptions != null) {
                return $userExceptions;
            }
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Change user password
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function changePassword(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::changePassword());
        $data = $request->all();

        /** @var User $user */
        $user = auth()->user();

        try {
            $passwordChange = $user->changePassword($data);

            return api_response(100, "Okay", $passwordChange);
        } catch (Exception $exception) {
            log_debug($exception, "UserController::changePassword");

            $userExceptions = self::returnUserExceptions($exception);
            if ($userExceptions != null) {
                return $userExceptions;
            }
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Delete user account
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function deleteAccount(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::deleteAccount());

        $data = $request->all();
        /** @var User $user */
        $user = auth()->user();

        try {
            $user->deleteAccount($data);

            return api_response(100, "Okay");
        } catch (Exception $exception) {
            log_debug($exception, "UserController::deleteAccount");

            $userExceptions = self::returnUserExceptions($exception);
            if ($userExceptions != null) {
                return $userExceptions;
            }
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Block account
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function blockAccount(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::blockAccount());

        try {
            BlockedAccounts::blockAccount($request->{"blocked_user_id"});

            return api_response(100, "Okay");
        } catch (Exception $exception) {
            log_debug($exception, "UserController::blockAccount");

            $userExceptions = self::returnUserExceptions($exception);
            if ($userExceptions != null) {
                return $userExceptions;
            }
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Unblock account
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function unblockAccount(Request $request): Response|Application|ResponseFactory
    {
        $request->validate(UserRules::unblockAccount());
        try {
            $result = BlockedAccounts::unblockAccount($request->{"unblocked_user_id"});
            if ($result == null) {
                return api_response(120, __("errors.unknown_error"));
            }
            return api_response(100, "Okay");
        } catch (Exception $exception) {
            log_debug($exception, "UserController::unblockAccount");
        }

        return api_response(120, __("errors.unknown_error"));
    }

    /**
     * Get user's blocked accounts
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function userBlockedAccounts(Request $request): Response|Application|ResponseFactory
    {
        try {
            $blockedAccounts = BlockedAccounts::userBlockedAccounts(
                $request->get("page")
            );

            $finalData = default_paginator_format(
                $blockedAccounts->lastPage(),
                $blockedAccounts->total(),
                $blockedAccounts->currentPage(),
                "blocked_accounts",
                BlockedAccounts::computeForBlockedAccounts(
                    $blockedAccounts->items()
                ),
            );

            return api_response(100, 'Ok', $finalData);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::userBlockedAccounts');
        }

        return api_response(101, __('errors.unknown_error'));
    }

    /**
     * Search blocked accounts
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function searchBlockedAccounts(Request $request): Response|Application|ResponseFactory
    {
        try {
            $blockedAccounts = BlockedAccounts::searchBlockedAccounts(
                $request->get('query') ?? "",
                $request->get('page')
            );

            $finalData = default_paginator_format(
                $blockedAccounts->lastPage(),
                $blockedAccounts->total(),
                $blockedAccounts->currentPage(),
                "blocked_accounts",
                BlockedAccounts::computeForBlockedAccounts(
                    $blockedAccounts->items()
                ),
            );

            return api_response(100, 'Ok', $finalData);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::searchBlockedAccounts');
        }

        return api_response(101, __('errors.unknown_error'));
    }

    /**
     * Discover phone contacts
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function discover(Request $request): Response|Application|ResponseFactory
    {
        $data = $request->all();

        try {
            $discovers = User::discover($data);

            return api_response(100, 'Ok', $discovers);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::discover');
        }

        return api_response(101, __('errors.unknown_error'));
    }

    /**
     * Get product reviews
     *
     * @param Request $request
     * @return Application|ResponseFactory|Response
     */
    public function reviews(Request $request): Response|Application|ResponseFactory
    {
        try {
            /** @var User $user */
            $user = auth()->user();

            $productRatings = $user->{'business'}->reviews($request->{"page"});

            return api_response(100, 'Ok', $productRatings);
        } catch (Exception $exception) {
            log_debug(exception: $exception, prefix: 'UserController::reviews');
        }

        return api_response(101, __('errors.unknown_error'));
    }

    /**
     * We try to do instant search of a user's followers
     *
     * @param Request $request
     * @return Response|Application|ResponseFactory
     */
    public function lookingUsersToIdentify(Request $request): Response|Application|ResponseFactory
    {
        $search = $request->{'search'};

        try {
            /** @var User $user */
            $user = auth()->user();

            $data = Follower::lookingUsersToIdentify($user, $search ?? '');

            return api_response(100, "Okay", $data);
        } catch (\Exception $exception) {
            log_debug($exception, "UserController::lookingUsersToIdentify");
        }

        return api_response(101, __('errors.unknown_error'));
    }

    // Controller utilities

    /**
     * Return exception
     *
     * @param Exception $exception
     * @return Response|Application|ResponseFactory|null
     */
    protected static function returnUserExceptions(Exception $exception): Response|Application|ResponseFactory|null
    {
        if ($exception instanceof InvalidFollowException || $exception instanceof TelephoneAlreadyExistException || $exception instanceof EmailAlreadyExistException || $exception instanceof InvalidPasswordException || $exception instanceof PasswordAlreadyUsedException || $exception instanceof PasswordConfirmationException || $exception instanceof InvalidAccountBlockingAttempt || $exception instanceof WalletCurrencyChangeException || $exception instanceof UserNameAlreadyExistException || $exception instanceof UploadFileException) {
            return api_response(120, $exception->getMessage());
        }

        return null;
    }
}
