/*
 * 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 com.juick.model.*;
import com.juick.service.*;
import com.juick.util.HttpBadRequestException;
import com.juick.util.HttpForbiddenException;
import io.swagger.v3.oas.annotations.Hidden;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.inject.Inject;
import java.util.Collections;
import java.util.List;

/**
 * Created by vitalyster on 24.10.2016.
 */
@RestController
public class Notifications {
    private static final Logger logger = LoggerFactory.getLogger("www");
    
    @Inject
    private PushQueriesService pushQueriesService;
    @Inject
    private MessagesService messagesService;
    @Inject
    private SubscriptionService subscriptionService;
    @Inject
    private UserService userService;
    @Inject
    private TelegramService telegramService;
    @Inject
    private User serviceUser;

    private User collectTokens(Integer uid) {
        User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE);
        user.setUnreadCount(messagesService.getUnread(user).size());
        pushQueriesService.getGCMRegID(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "gcm", t, null)));
        pushQueriesService.getAPNSToken(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "apns", t, null)));
        pushQueriesService.getMPNSURL(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "mpns", t, null)));
        List<ExternalToken> xmppJids = userService.getJIDsbyUID(uid).stream()
                .map(jid -> new ExternalToken(null, "xmpp", jid, null))
                .toList();
        user.getTokens().addAll(xmppJids);
        List<ExternalToken> tgIds = telegramService.getTelegramIdentifiers(Collections.singletonList(user)).stream()
                .map(tgId -> new ExternalToken(null, "durov", String.valueOf(tgId), null))
                .toList();
        user.getTokens().addAll(tgIds);
        return user;
    }

    @Hidden
    @RequestMapping(value = "/api/notifications", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public List<User> doGet(
            @ModelAttribute(binding = false) User visitor,
            @RequestParam(required = false, defaultValue = "0") int uid,
            @RequestParam(required = false, defaultValue = "0") int mid,
            @RequestParam(required = false, defaultValue = "0") int rid) {
        if (!(visitor.equals(serviceUser))) {
            throw new HttpForbiddenException();
        }
        if (uid > 0 && mid == 0) {
            // PM
            return Collections.singletonList(collectTokens(uid));
        } else {
            if (mid > 0) {
                // reply
                Message msg = messagesService.getMessage(mid).orElseThrow(IllegalStateException::new);
                List<User> users = Collections.emptyList();
                if (rid > 0) {
                    Message op = messagesService.getMessage(mid).orElseThrow(IllegalStateException::new);
                    Message reply = messagesService.getReply(mid, rid);
                    if (reply != null) {
                        users = subscriptionService.getUsersSubscribedToComments(op, reply);
                    } else {
                        logger.warn("Reply not found: {}/{}", mid, rid);
                    }
                } else {
                    users = subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg);
                }

                return users.stream().map(User::getUid)
                        .map(this::collectTokens).toList();
            } else {
                // read
                return Collections.singletonList(collectTokens(uid));
            }
        }
    }

    @Hidden
    @RequestMapping(value = "/api/notifications", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Status doDelete(
            @ModelAttribute User visitor,
            @RequestBody List<ExternalToken> list) {
        if (!visitor.equals(serviceUser)) {
            throw new HttpForbiddenException();
        }
        list.forEach(t -> {
            switch (t.type()) {
                case "gcm" -> pushQueriesService.deleteGCMToken(t.token());
                case "apns" -> pushQueriesService.deleteAPNSToken(t.token());
                case "mpns" -> pushQueriesService.deleteMPNSToken(t.token());
                default -> throw new HttpBadRequestException();
            }
        });

        return Status.OK;
    }
    @Hidden
    @RequestMapping(value = "/api/notifications/delete", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    public Status doDeleteTokens(
            @ModelAttribute User visitor,
            @RequestBody List<ExternalToken> list) {
        if (!visitor.equals(serviceUser)) {
            throw new HttpForbiddenException();
        }
        list.forEach(t -> {
            switch (t.type()) {
                case "gcm" -> pushQueriesService.deleteGCMToken(t.token());
                case "apns" -> pushQueriesService.deleteAPNSToken(t.token());
                case "mpns" -> pushQueriesService.deleteMPNSToken(t.token());
                default -> throw new HttpBadRequestException();
            }
        });

        return Status.OK;
    }

    @Hidden
    @RequestMapping(value = "/api/notifications", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
    public Status doPut(
            @ModelAttribute User visitor,
            @RequestBody List<ExternalToken> list) {
        list.forEach(t -> {
            switch (t.type()) {
                case "gcm" -> pushQueriesService.addGCMToken(visitor.getUid(), t.token());
                case "apns" -> pushQueriesService.addAPNSToken(visitor.getUid(), t.token());
                case "mpns" -> pushQueriesService.addMPNSToken(visitor.getUid(), t.token());
                default -> throw new HttpBadRequestException();
            }
        });
        return Status.OK;
    }

    @Deprecated
    @RequestMapping(value = "/api/android/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public Status doAndroidRegister(
            @ModelAttribute User visitor,
            @RequestParam(name = "regid") String regId) {
        pushQueriesService.addGCMToken(visitor.getUid(), regId);
        return Status.OK;
    }

    @Deprecated
    @RequestMapping(value = "/api/winphone/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public Status doWinphoneRegister(
            @ModelAttribute User visitor,
            @RequestParam(name = "url") String regId) {
        pushQueriesService.addMPNSToken(visitor.getUid(), regId);
        return Status.OK;
    }
}