<?php

namespace App\Http\Controllers\Gallery;

use App\Album;
use App\Facade\Theme;
use App\Facade\UserConfig;
use App\Helpers\DbHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\SaveUserSettingsRequest;
use App\Mail\UserChangeEmailRequired;
use App\User;
use App\UserActivity;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Symfony\Component\HttpFoundation\Request;

class UserController extends Controller
{
    public function activityFeed()
    {
        if (!UserConfig::get('social_user_feeds'))
        {
            return redirect(route('home'));
        }

        return Theme::render('gallery.user_activity_feed', [
            'user' => $this->getUser()
        ]);
    }

    public function activityFeedJson()
    {
        if (!UserConfig::get('social_user_feeds'))
        {
            return response()->json(['message' => 'Activity feeds not enabled']);
        }

        $user = $this->getUser();

        $result = [];
        $activities = UserActivity::with('photo')
            ->with('photoComment')
            ->with('user')
            ->join('user_followers', 'user_followers.following_user_id', '=', 'user_activity.user_id')
            ->where([
                'user_followers.user_id' => $user->id
            ])
            ->orderBy('activity_at', 'desc')
            ->limit(100) // TODO: make this configurable
            ->select('user_activity.*')
            ->get();

        /** @var UserActivity $activity */
        foreach ($activities as $activity)
        {
            $userName = $activity->user->name;
            $userProfileUrl = $activity->user->profileUrl();
            $userAvatar = Theme::gravatarUrl($activity->user->email, 32);

            $newItem = [
                'activity_at' => date(UserConfig::get('date_format'), strtotime($activity->activity_at)),
                'avatar' => $userAvatar,
                'description' => trans(sprintf('gallery.user_feed_type.%s', $activity->type))
            ];

            $params = [];
            $params['user_name'] = $userName;
            $params['user_url'] = $userProfileUrl;

            if (!is_null($activity->photo))
            {
                // Check the user has access
                if (!$this->getUser()->can('view', $activity->photo))
                {
                    continue;
                }

                $params['photo_name'] = $activity->photo->name;
                $params['photo_url'] = $activity->photo->url();
            }

            if (!is_null($activity->album))
            {
                // Check the user has access
                if (!$this->getUser()->can('view', $activity->album))
                {
                    continue;
                }

                $params['album_name'] = $activity->album->name;
                $params['album_url'] = $activity->album->url();
            }

            // Other activity-specific parameters
            switch (strtolower($activity->type))
            {
                case 'user.created':
                    $params['app_name'] = UserConfig::get('app_name');
                    $params['app_url'] = route('home');
                    break;
            }

            $newItem['params'] = $params;

            $result[] = $newItem;
        }

        return response()->json($result);
    }

    public function confirmEmailChangeState(Request $request)
    {
        $user = $this->getUser();
        if (!$user->is_email_change_in_progress)
        {
            return redirect(route('userSettings'));
        }

        // Update the e-mail address
        $user->email = $user->new_email_address;

        // Reset the e-mail change state
        $user->is_email_change_in_progress = false;
        $user->new_email_address = null;
        $user->save();

        $request->session()->flash('success', trans('auth.change_email_success_message'));
        return redirect(route('userSettings'));
    }

    public function followUser($idOrAlias)
    {
        $user = $this->loadUserProfilePage($idOrAlias);

        $isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
        if (!$isFollowing)
        {
            $this->getUser()->following()->attach(
                $user->id,
                [
                    'created_at' => new \DateTime(),
                    'updated_at' => new \DateTime()
                ]
            );
        }

        return response()->json(true);
    }

    public function resetEmailChangeState(Request $request)
    {
        $user = $this->getUser();
        if (!$user->is_email_change_in_progress)
        {
            return redirect(route('userSettings'));
        }

        $data = $request->all();

        if (isset($data['resend_email']))
        {
            $this->sendEmailChangeConfirmationEmail($user, $user->new_email_address);
            $request->session()->flash('info', trans('auth.change_email_required_message'));
        }

        if (isset($data['cancel_change']))
        {
            $user->is_email_change_in_progress = false;
            $user->new_email_address = null;
            $user->save();
        }

        return redirect(route('userSettings'));
    }

    public function saveSettings(SaveUserSettingsRequest $request)
    {
        $data = $request->only(['name', 'email', 'profile_alias', 'enable_profile_page']);
        $user = $this->getUser();

        if (
            UserConfig::get('require_email_verification') &&
            isset($data['email']) &&
            $data['email'] != $user->email &&
            !$user->is_email_change_in_progress
        )
        {
            // Can't update the e-mail directly until the new e-mail address has been verified.
            // TODO - send e-mail and handle response, flag e-mail as being "change in-progress"
            // Send activation e-mail

            $this->sendEmailChangeConfirmationEmail($user, $data['email']);
            $request->session()->flash('info', trans('auth.change_email_required_message'));

            // Flag the user as a change e-mail in progress
            $user->new_email_address = $data['email'];
            $user->is_email_change_in_progress = true;
            $user->save();

            unset($data['email']);
            $request->session()->flash('info', trans('auth.change_email_required_message'));
        }

        // Don't allow e-mail address to be changed if a change is in progress
        if ($user->is_email_change_in_progress)
        {
            unset($data['email']);
        }

        $user->fill($data);
        $user->enable_profile_page = (isset($data['enable_profile_page']) && strtolower($data['enable_profile_page']) == 'on');
        $user->save();

        $request->session()->flash('success', trans('gallery.user_settings.settings_saved'));

        return redirect(route('userSettings'));
    }

    public function settings(Request $request)
    {
        return Theme::render('gallery.user_settings', [
            'info' => $request->session()->get('info'),
            'success' => $request->session()->get('success'),
            'user' => $this->getUser()
        ]);
    }

    public function show(Request $request, $idOrAlias)
    {
        $user = $this->loadUserProfilePage($idOrAlias);

        $albums = $this->getAlbumsForUser($user);
        $albumIDs = $this->getAlbumIDsForUser($user);
        $cameras = $this->getCamerasUsedInAlbums($albumIDs);
        $activity = $this->getActivityDatesInAlbums($albumIDs);

        $daysInMonth = $this->getDaysInMonths();

        // Only logged-in users can follow other users (and if it's not their own page!)
        $canFollow = !$this->getUser()->isAnonymous() && $this->getUser()->id != $user->id;
        $isFollowing = false;

        if ($canFollow)
        {
            // Is the current user following this user?
            $isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
        }

        return Theme::render('gallery.user_profile', [
            'active_tab' => $request->get('tab'),
            'activity_taken' => $this->constructActivityGrid($activity['taken']),
            'activity_uploaded' => $this->constructActivityGrid($activity['uploaded']),
            'albums' => $albums,
            'cameras' => $cameras,
            'can_follow' => $canFollow,
            'is_following' => $isFollowing,
            'month_days' => $daysInMonth,
            'user' => $user
        ]);
    }

    public function showFeedJson(Request $request, $idOrAlias)
    {
        $user = $this->loadUserProfilePage($idOrAlias);

        $result = [];
        $activities = UserActivity::with('photo')
            ->with('photoComment')
            ->with('album')
            ->where([
                'user_id' => $user->id
            ])
            ->orderBy('activity_at', 'desc')
            ->limit(100) // TODO: make this configurable
            ->get();

        $userName = $user->name;
        $userProfileUrl = $user->profileUrl();
        $userAvatar = Theme::gravatarUrl($user->email, 32);

        /** @var UserActivity $activity */
        foreach ($activities as $activity)
        {
            $newItem = [
                'activity_at' => date(UserConfig::get('date_format'), strtotime($activity->activity_at)),
                'avatar' => $userAvatar,
                'description' => trans(sprintf('gallery.user_feed_type.%s', $activity->type))
            ];

            $params = [];
            $params['user_name'] = $userName;
            $params['user_url'] = $userProfileUrl;

            if (!is_null($activity->photo))
            {
                // Check the user has access
                if (!$this->getUser()->can('view', $activity->photo))
                {
                    continue;
                }

                $params['photo_name'] = $activity->photo->name;
                $params['photo_url'] = $activity->photo->url();
            }

            if (!is_null($activity->album))
            {
                // Check the user has access
                if (!$this->getUser()->can('view', $activity->album))
                {
                    continue;
                }

                $params['album_name'] = $activity->album->name;
                $params['album_url'] = $activity->album->url();
            }

            // Other activity-specific parameters
            switch (strtolower($activity->type))
            {
                case 'user.created':
                    $params['app_name'] = UserConfig::get('app_name');
                    $params['app_url'] = route('home');
                    break;
            }

            $newItem['params'] = $params;

            $result[] = $newItem;
        }

        return response()->json($result);
    }

    public function unFollowUser($idOrAlias)
    {
        $user = $this->loadUserProfilePage($idOrAlias);

        $isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
        if ($isFollowing)
        {
            $this->getUser()->following()->detach($user->id);
        }

        return response()->json(true);
    }

    private function constructActivityGrid(Collection $collection)
    {
        $results = [];

        $lastYearFrom = new \DateTime();
        $lastYearFrom->sub(new \DateInterval('P1Y'));
        $lastYearFrom->add(new \DateInterval('P1D'));

        $today = new \DateTime();
        $current = clone $lastYearFrom;

        while ($current < $today)
        {
            $year = intval($current->format('Y'));
            $month = intval($current->format('m'));
            $date = intval($current->format('d'));

            if (!isset($results[$year]))
            {
                $results[$year] = [];
            }

            if (!isset($results[$year][$month]))
            {
                $results[$year][$month] = [];
            }

            if (!isset($results[$year][$month][$date]))
            {
                $results[$year][$month][$date] = 0;
            }

            $current->add(new \DateInterval('P1D'));
        }

        // Now update the totals from the collection
        foreach ($collection as $photoInfo)
        {
            $date = \DateTime::createFromFormat('Y-m-d', $photoInfo->the_date);

            $year = intval($date->format('Y'));
            $month = intval($date->format('m'));
            $date = intval($date->format('d'));

            $results[$year][$month][$date] = $photoInfo->photos_count;
        }

        // Replace the month names
        foreach ($results as $year => &$months)
        {
            foreach ($months as $month => $dates)
            {
                $monthDate = \DateTime::createFromFormat('m', $month);
                $months[$monthDate->format('M')] = $dates;
                unset($months[$month]);
            }
        }

        return $results;
    }

    private function getActivityDatesInAlbums(array $albumIDs)
    {
        $createdAt = DB::table('photos')
            ->whereIn('album_id', $albumIDs)
            ->whereRaw(DB::raw('DATE(created_at) > DATE(DATE_SUB(NOW(), INTERVAL 1 year))'))
            ->select([
                DB::raw('DATE(created_at) AS the_date'),
                DB::raw('COUNT(photos.id) AS photos_count')
            ])
            ->groupBy(DB::raw('DATE(created_at)'))
            ->orderBy(DB::raw('DATE(created_at)'))
            ->get();

        $takenAt = DB::table('photos')
            ->whereIn('album_id', $albumIDs)
            ->whereRaw(DB::raw('DATE(taken_at) > DATE(DATE_SUB(NOW(), INTERVAL 1 year))'))
            ->select([
                DB::raw('DATE(taken_at) AS the_date'),
                DB::raw('COUNT(photos.id) AS photos_count')
            ])
            ->groupBy(DB::raw('DATE(taken_at)'))
            ->orderBy(DB::raw('DATE(taken_at)'))
            ->get();

        return ['uploaded' => $createdAt, 'taken' => $takenAt];
    }

    private function getAlbumsForUser(User $user)
    {
        return DbHelper::getAlbumsForCurrentUser_NonPaged()
            ->where('user_id', $user->id)
            ->paginate(UserConfig::get('items_per_page'));
    }

    private function getAlbumIDsForUser(User $user)
    {
        $results = [];

        $albums = DbHelper::getAlbumsForCurrentUser_NonPaged()
            ->where('user_id', $user->id)
            ->select('albums.id')
            ->get();

        foreach ($albums as $album)
        {
            $results[] = intval($album->id);
        }

        return $results;
    }

    private function getCamerasUsedInAlbums(array $albumIDs)
    {
        return DB::table('photos')
            ->whereIn('album_id', $albumIDs)
            ->where([
                ['camera_make', '!=', ''],
                ['camera_model', '!=', '']
            ])
            ->groupBy('camera_make', 'camera_model', 'camera_software')
            ->select('camera_make', 'camera_model', 'camera_software', DB::raw('count(*) as photo_count'))
            ->orderBy('photo_count', 'desc')
            ->orderBy('camera_make')
            ->orderBy('camera_model')
            ->orderBy('camera_software')
            ->get();
    }

    private function getDaysInMonths()
    {
        $results = [];

        $lastYearFrom = new \DateTime();
        $lastYearFrom->sub(new \DateInterval('P1Y'));
        $lastYearFrom->sub(new \DateInterval(sprintf('P%dD', $lastYearFrom->format('d') - 1)));

        $today = new \DateTime();
        $current = clone $lastYearFrom;

        while ($current < $today)
        {
            $year = intval($current->format('Y'));
            $month = intval($current->format('m'));

            $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
            $results[$year][$current->format('M')] = $daysInMonth;

            $current->add(new \DateInterval('P1M'));
        }

        return $results;
    }

    /**
     * @param $idOrAlias
     * @return User
     */
    private function loadUserProfilePage($idOrAlias)
    {
        // If a user has a profile alias set, their profile page cannot be accessed by the ID
        $user = User::where(DB::raw('COALESCE(NULLIF(profile_alias, \'\'), id)'), strtolower($idOrAlias))->first();

        if (is_null($user))
        {
            App::abort(404);
            return null;
        }

        $this->authorizeForUser($this->getUser(), 'view', $user);

        return $user;
    }

    private function sendEmailChangeConfirmationEmail(User $user, $newEmailAddress)
    {
        $oldEmailAddress = $user->email;
        $user->email = $newEmailAddress;

        Mail::to($user)->send(new UserChangeEmailRequired($user));
        $user->email = $oldEmailAddress;
    }
}