/*
 * Copyright (C) 2008-2020, Juick
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.juick.www.api;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.inject.Inject;

import com.juick.model.AnonymousUser;
import com.juick.model.ApplicationStatus;
import com.juick.model.User;
import com.juick.service.EmailService;
import com.juick.service.MessagesService;
import com.juick.service.StorageService;
import com.juick.service.TagService;
import com.juick.service.TelegramService;
import com.juick.service.UserService;
import com.juick.service.activities.UpdateUserEvent;
import com.juick.service.component.MailVerificationEvent;
import com.juick.util.HttpBadRequestException;
import com.juick.util.HttpNotFoundException;
import com.juick.util.HttpUtils;
import com.juick.util.WebUtils;
import com.juick.www.WebApp;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author ugnich
 */
@RestController
public class Users {
    @Inject
    private UserService userService;
    @Inject
    private MessagesService messagesService;
    @Inject
    private TelegramService telegramService;
    @Inject
    private EmailService emailService;
    @Inject
    private TagService tagService;
    @Inject
    private WebApp webApp;
    @Inject
    private StorageService storageService;
    @Inject
    private ApplicationEventPublisher applicationEventPublisher;

    @RequestMapping(value = "/api/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public List<User> doGetUsers(
            @ModelAttribute User visitor,
            @RequestParam(value = "uname", required = false) List<String> unames) {
        List<User> users = new ArrayList<>();

        if (unames != null) {
            unames.removeIf(WebUtils::isNotUserName);

            if (!unames.isEmpty() && unames.size() < 20)
                users.addAll(userService.getUsersByName(unames));
        }
        users.forEach(u -> u.setAvatar(webApp.getAvatarUrl(u)));
        if (!users.isEmpty())
            return users;
        if (!visitor.isAnonymous()) {
            visitor.setAvatar(webApp.getAvatarUrl(visitor));
            return Collections.singletonList(visitor);
        }

        throw new HttpNotFoundException();
    }

    @GetMapping("/api/me")
    public SecureUser getMe(@ModelAttribute User visitor) {
        SecureUser me = new SecureUser();
        me.setUid(visitor.getUid());
        me.setName(visitor.getName());
        me.setAuthHash(userService.getHashByUID(visitor.getUid()));
        List<Integer> unread = messagesService.getUnread(visitor);
        me.setUnread(unread);
        me.setUnreadCount(unread.size());
        var friends = userService.getUserFriends(visitor.getUid());
        friends.forEach(r -> r.setAvatar(webApp.getAvatarUrl(r)));
        me.setRead(friends);
        var readers = userService.getUserReaders(visitor.getUid());
        readers.forEach(r -> r.setAvatar(webApp.getAvatarUrl(r)));
        me.setReaders(readers);
        me.setAvatar(webApp.getAvatarUrl(visitor));
        me.getTagStats().addAll(tagService.getUserTagStats(me.getUid()));
        return (SecureUser)userService.getUserInfo(me);
    }
    @PostMapping("/api/me")
    public void updateMe(@ModelAttribute User visitor,
                                         @RequestParam(required = false) String password,
                                         @RequestParam(value = "jid-del", required = false) String jidForDeletion,
                                         @RequestParam(value = "email-add", required = false) String newEmail,
                                         @RequestParam(value = "email-del", required = false) String emailForDeletion,
                                         @RequestParam(value = "account-del", required = false) String accountToDelete) {
        if (StringUtils.isNotEmpty(password)) {
            if (!userService.updatePassword(visitor, password)) {
                throw new HttpBadRequestException();
            }
        }
        if (StringUtils.isNotEmpty(jidForDeletion)) {
            if (!userService.deleteJID(visitor.getUid(), jidForDeletion)) {
                throw new HttpBadRequestException();
            }
        }
        if (StringUtils.isNotEmpty(newEmail)) {
            if (!emailService.verifyAddressByCode(visitor.getUid(), newEmail)) {
                String authCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase();
                if (emailService.addVerificationCode(visitor.getUid(), newEmail, authCode)) {
                    applicationEventPublisher.publishEvent(new MailVerificationEvent(this, newEmail , authCode));
                }
            }
        }
        if (StringUtils.isNotEmpty(emailForDeletion)) {
            if (!emailService.deleteEmail(visitor.getUid(), emailForDeletion)) {
                throw new HttpBadRequestException();
            }
        }
        if (StringUtils.isNotEmpty(accountToDelete)) {
            switch (accountToDelete) {
                case "twitter":
                    userService.deleteTwitterToken(visitor.getUid());
                    break;
                case "vk":
                    userService.deleteVKUser(visitor.getUid());
                    break;
                case "durov":
                    telegramService.deleteTelegramUser(visitor.getUid());
                    break;
            }
        }
    }
    @PostMapping("/api/me/subscribe")
    public void subscribeMe(@ModelAttribute User visitor, String email) {
        // TODO: check status
        emailService.setNotificationsEmail(visitor.getUid(), email);
    }
    @PostMapping("/api/me/upload")
    public void updateInfo(@ModelAttribute User visitor,
                           @RequestParam MultipartFile avatar) throws IOException {
        String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, storageService.getTemporaryDirectory()).getHost();
        if (StringUtils.isNotEmpty(avatarTmpPath)) {
            storageService.saveAvatar(avatarTmpPath, visitor);
            applicationEventPublisher.publishEvent(new UpdateUserEvent(this, visitor));
        }
    }

    @RequestMapping(value = "/api/users/read", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public List<User> doGetUserRead(
            @ModelAttribute User visitor,
            @RequestParam String uname) {
        int uid = 0;
        if (uname == null) {
            uid = visitor.getUid();
        } else {
            if (WebUtils.isUserName(uname)) {
                User u = userService.getUserByName(uname);
                if (!u.isAnonymous()) {
                    uid = u.getUid();
                }
            }
        }

        if (uid > 0) {
            List<User> friends = userService.getUserFriends(uid);
            friends.forEach(f -> f.setAvatar(webApp.getAvatarUrl(f)));
            return friends;
        }
        throw new HttpNotFoundException();
    }

    @RequestMapping(value = "/api/users/readers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public List<User> doGetUserReaders(
            @ModelAttribute User visitor,
            @RequestParam String uname) {
        int uid = 0;
        if (uname == null) {
            uid = visitor.getUid();
        } else {
            if (WebUtils.isUserName(uname)) {
                User u = userService.getUserByName(uname);
                if (!u.isAnonymous()) {
                    uid = u.getUid();
                }
            }
        }

        if (uid > 0) {
            List<User> readers = userService.getUserReaders(uid);
            readers.forEach(r -> r.setAvatar(webApp.getAvatarUrl(r)));
            return readers;
        }
        throw new HttpNotFoundException();
    }

    @GetMapping("/api/info/{uname}")
    public User getUserInfo(@ModelAttribute User visitor, @PathVariable String uname) {
        User user = userService.getUserByName(uname);
        if (!user.isBanned()) {
            user.setRead(doGetUserRead(visitor, uname));
            user.setReaders(doGetUserReaders(visitor, uname));
            user.setAvatar(webApp.getAvatarUrl(user));
            return userService.getUserInfo(user);
        }
        throw new HttpNotFoundException();
    }

    @Deprecated
    @GetMapping(value = "/api/avatar", produces = MediaType.IMAGE_PNG_VALUE)
    public byte[] getAvatarUrl(
            @RequestParam(required = false) String uname,
            @RequestParam(required = false) String jid)
            throws IOException {
        User user = AnonymousUser.INSTANCE;
        if (StringUtils.isNotEmpty(uname)) {
            user = userService.getUserByName(uname);
        }
        if (user.isAnonymous() && StringUtils.isNotEmpty(jid)) {
            user = userService.getUserByJID(jid);
        }
        return IOUtils.toByteArray(URI.create(webApp.getAvatarUrl(user)));
    }
    public class SecureUser extends User {

        public String getHash() {
            return getAuthHash();
        }
        public List<String> getJIDs() {
            return userService.getAllJIDs(this);
        }
        public List<String> getEmails() {
            return userService.getEmails(this);
        }
        public String getActiveEmail() {
            return emailService.getNotificationsEmail(this.getUid());
        }
        public String getTwitterName() {
            return userService.getTwitterName(this.getUid());
        }
        public String getTelegramName() {
            return userService.getTelegramName(this.getUid());
        }
        public ApplicationStatus getFacebookStatus() {
            return userService.getFbCrossPostStatus(this.getUid());
        }
    }
}