package net.sf.rails.common.notify;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.rails.common.Config;
import net.sf.rails.game.Player;
import net.sf.rails.game.PlayerManager;
import net.sf.rails.game.RailsRoot;
import net.sf.rails.game.state.Observable;
import net.sf.rails.game.state.Observer;
import net.sf.rails.ui.swing.GameUIManager;

public class Slack {
    private static final Logger log = LoggerFactory.getLogger(Slack.class);

    private final RailsRoot root;
    private final GameUIManager gameUiManager;

    private CurrentPlayerModelObserver observer;

    private final CloseableHttpClient httpClient;

    private String webhook = null;
    private Map<String, String> playerNameMappings = new HashMap<>();
    private String body = null;
    private static final String MESSAGE_TEMPLATE = "Your turn ${current}";
    private static final String BODY_TEMPLATE = "{\"text\":\"@@\"}";

    public void setConfig() {
        webhook = StringUtils.trimToNull(Config.get("notify.slack.webhook"));
        String message = StringUtils.defaultIfBlank(Config.get("notify.message"), MESSAGE_TEMPLATE);
        body = StringUtils.replace(BODY_TEMPLATE, "@@", message);

        parseUserMappings(Config.get("notify.slack.user_mapping"));
    }

    public void parseUserMappings(String mappings) {
        if ( StringUtils.isBlank(mappings) ) {
            return;
        }
        playerNameMappings = Arrays.stream(mappings.split(","))
                .map(s -> s.split(":"))
                .collect(Collectors.toMap(e -> e[0], e -> "<@" + e[1] + ">"));
    }

    private class CurrentPlayerModelObserver implements Observer {
        private Player formerCurrentPlayer = null;
        private final PlayerManager pm;

        public CurrentPlayerModelObserver(PlayerManager pm) {
            this.pm = pm;
            if ( pm != null ) {
                formerCurrentPlayer = pm.getCurrentPlayer();
            }
        }

        public void update(String text) {
            String localPlayer = Config.get("local.player.name");
            log.debug("Slack called with f:{}/c:{}/l:{}", formerCurrentPlayer.getId(), pm.getCurrentPlayer().getId(), localPlayer);
            if ( formerCurrentPlayer != pm.getCurrentPlayer() ) {
                if ( formerCurrentPlayer.getId().equals(localPlayer ) ) {
                    // only alert if we are transitioning from the former player
                    if ( !localPlayer.equals(pm.getCurrentPlayer().getId()) ) {
                        // only send a notification if we are switching to a different user, ie not ourselves
                        sendMessage(pm.getCurrentPlayer().getId());
                    }
                }
                formerCurrentPlayer = pm.getCurrentPlayer();
            }
        }

        public Observable getObservable() {
            return pm.getCurrentPlayerModel();
        }

        public Player getFormerPlayer() {
            return formerCurrentPlayer;
        }
    }

    public Slack(final GameUIManager gameUIManger, final RailsRoot root) {
        this.gameUiManager = gameUIManger;
        this.root = root;

        httpClient = HttpClients.createDefault();

        final PlayerManager pm = root.getPlayerManager();
        if ( pm.getCurrentPlayerModel() != null ) {
            observer = new CurrentPlayerModelObserver(pm);
            pm.getCurrentPlayerModel().addObserver(observer);
        }
    }

    public void sendMessage(String player) {
        setConfig();
        if ( webhook == null ) {
            return;
        }
        Map<String, String> keys = new HashMap<>();
        keys.put("game", root.getGameName());
        //keys.put("gameName", StringUtils.defaultIfBlank(root.getGameData().getUsersGameName(), "[none]"));
        keys.put("round", gameUiManager.getCurrentRound().getRoundName());
        keys.put("current", StringUtils.defaultIfBlank(playerNameMappings.get(player), player));
        keys.put("previous", StringUtils.defaultIfBlank(observer.getFormerPlayer().getId(), "[none]"));

        String msgBody = StringSubstitutor.replace(body, keys);
        log.debug("Sending message '{}' to Slack for user {}", msgBody, player);

        HttpPost httpPost = new HttpPost(webhook);
        try {
            httpPost.setEntity(new StringEntity(msgBody));
            httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
            httpPost.setHeader(HttpHeaders.USER_AGENT, "18xx Rails");
            CloseableHttpResponse response = httpClient.execute(httpPost);
            if ( response.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT ) {
                // TODO: verify result
                log.debug("Unexpected Slack response: {}", response);
            }
            response.close();
        }
        catch (IOException e) {
            log.error("Error sending message to Slack", e);
        }
    }
}