package com.adobe.primetime.reference;

import android.os.AsyncTask;
import android.os.Build;
import android.app.Application;
import android.util.Base64;

//import com.adobe.adobepass.accessenabler.utils.Utils;
import com.adobe.mediacore.MediaPlayer;
import com.adobe.mediacore.MediaPlayerEvent;
import com.adobe.mediacore.MediaPlayerItem;
import com.adobe.mediacore.NotificationEvent;
import com.adobe.mediacore.PSDKErrorCode;
import com.adobe.mediacore.metadata.Metadata;
import com.adobe.mediacore.metadata.Notification;
import com.adobe.mediacore.NotificationEventListener;
import com.adobe.mediacore.Version;
import com.adobe.mediacore.qos.DeviceInformation;
import com.adobe.primetime.reference.logging.Logger;
import com.adobe.primetime.reference.manager.EntitlementManager;
import com.adobe.primetime.reference.manager.PlaybackManager;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONObject;
import org.json.JSONException;

import java.io.*;
import java.util.*;

import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.GZIPOutputStream;

/**
 * Created by hai on 10/17/18.
 */

public class SplunkHttpServiceConnector{

    /**
     * @private
     */
    private MediaPlayer _player;

    private String  _clientId;
    private List<JSONObject> _list;
    private Set<String> _numericKeys;

    private String _endPointUrl;
    private String _authorizationString;
    private boolean _useGzipEncoding;

    class Helper  extends TimerTask
    {
        public void run()
        {
            System.out.println("Timer ran ");
            if(_list.size() > 0)
            {
                sendNotifications(_list);
                _list.clear();
            }

        }
    }

    /**
     * @public
     * @param player
     */
    public final void configure(MediaPlayer player, String endpointUrl, String authString, boolean useGzipEncoding, long timerDurationMS, DeviceInformation deviceInfo)
    {
        _player = player;
        _clientId = UUID.randomUUID().toString();
        _list = new ArrayList<JSONObject>();
        _player.addEventListener(MediaPlayerEvent.REVENUE_OPTIMIZATION_REPORTING, collectEventHandler);
        _useGzipEncoding = useGzipEncoding;

        // certain known keys should be passed as numeric, not as string
        // this is temporary until we add metadata.getDouble()
        _numericKeys = new HashSet<String>();
        _numericKeys.add("clientTimestamp");
        _numericKeys.add("adRequestTimeout");
        _numericKeys.add("duration");
        _numericKeys.add("totalAdDuration");
        _numericKeys.add("placementDuration");
        _numericKeys.add("placementTime");
        _numericKeys.add("time");
        _numericKeys.add("responseStatus");
        _numericKeys.add("responseTotalTime");
        _numericKeys.add("responseStart");
        _numericKeys.add("totalAds");
        _numericKeys.add("proposedAds");
        _numericKeys.add("proposedDuration");

        _endPointUrl = endpointUrl;
        _authorizationString = authString;

        Timer timer = new Timer();
        TimerTask task = new Helper();

        timer.schedule(task, timerDurationMS, timerDurationMS);

        PrimetimeReference.logger.i("SplunkHttpServiceConnector _clientId ", _clientId);

        generateClientStartEvent(deviceInfo);
    }

    private final void generateClientStartEvent(DeviceInformation deviceInfo)
    {

        JSONObject root = new JSONObject();
        try {
            root.put("userAgent", Version.getDescription());
            root.put("tvsdkVersion", Version.getVersion());
            root.put("platform", deviceInfo.getModel() + ";" + " " + deviceInfo.getOS() + ", SDK: " + deviceInfo.getSDK() + ", Build: " + Build.ID);
        } catch (JSONException ignored) {
        }

        queueNotifications(generateAppLevelEvent("CLIENT_START", root));
    }

    private final List<JSONObject> generateAppLevelEvent(String eventType, JSONObject data)
    {
        JSONObject root = new JSONObject();
        try
        {
            data.put("event", eventType);
            data.put("clientTimestamp", System.currentTimeMillis() / 1000F);
            data.put("clientId", _clientId);
            root.put("event", data);
        } catch (JSONException ignored) {
        }

        List<JSONObject> list = new ArrayList<JSONObject>();
        list.add(root);

        return list;
    }

    private void queueNotifications(List<JSONObject> data)
    {
        PrimetimeReference.logger.i("SplunkHttpServiceConnector", "queueNotifications");

        _list.addAll(data);
    }

    private void sendNotifications(List<JSONObject> data)
    {
        PrimetimeReference.logger.i("SplunkHttpServiceConnector", "sendNotifications");

        Iterator<JSONObject> it = data.iterator();
        String messageBody = "";
        while (it.hasNext())
        {
            JSONObject json = it.next();
            if (messageBody.length() > 0)
            {
                messageBody = messageBody.concat("\n"); // split events by newline
            }
            messageBody = messageBody.concat(json.toString());
        }

        if (messageBody == null || messageBody.isEmpty())
        {
            return;
        }

        new SendEventTask().execute(_endPointUrl, _authorizationString, messageBody, _useGzipEncoding ? "1" : "");
    }

    /**
     * @private
     */
    private final NotificationEventListener collectEventHandler = new NotificationEventListener()
    {
        @Override
        public void onNotification(NotificationEvent notificationEvent)
        {
            queueNotifications(serializeNotification(notificationEvent.getNotification()));
        }
    };

    /**
     *
     * @param notification
     * @return
     */
    private final List<JSONObject> serializeNotification(Notification notification)
    {
        ArrayList<JSONObject> jsonList = new ArrayList<JSONObject>();
        do
        {
            Metadata metadata = notification.getMetadata();
            if (metadata != null)
            {
                try
                {
                    JSONObject wrapper = new JSONObject();
                    JSONObject json = convertMetadataToJSON(metadata);
                    json.put("clientId", _clientId);
                    wrapper.put("event", json);
                    jsonList.add(wrapper);
                }
                catch (Exception e)
                {
                }
            }

            notification = notification.getInnerNotification();

        } while (notification != null);

        return jsonList;
    }

    final public JSONObject convertMetadataToJSON(Metadata metadata) {
        JSONObject json = new JSONObject();
        Iterator<String> it = metadata.keySet().iterator();
        while (it.hasNext())
        {
            try
            {
                String key = it.next();
                String val = metadata.getValue(key);
                if (val != null)
                {
                    if (_numericKeys.contains(key))
                    {
                        json.put(key, Double.parseDouble(val));
                    }
                    else
                    {
                        json.put(key, val);
                    }
                }
                else
                {
                    Metadata childMetadata = metadata.getMetadata(key);
                    if (childMetadata != null) {
                        json.put(key, convertMetadataToJSON(childMetadata));
                    }
                }
            }
            catch (Exception e)
            {

            }
        }
        return json;
    }

    /**
     * @public
     */
    public final void destroy()
    {
        if (_player != null)
        {
            _player.removeEventListener(MediaPlayerEvent.REVENUE_OPTIMIZATION_REPORTING, collectEventHandler);
            _player = null;
        }
    }

    /**
     * Task to communicate with the Media Token Validator Service asynchronously over HTTP. This task is used after
     * a successful authorization call and a short-term media token is received.  The TVS takes the short-term media
     * token and the resource ID and returns either success or failure. If success, the token and resource ID is valid
     * access may be granted to the user to view the media.  If failure, the user may not have access to the media.
     *
     * <p>
     *     To use, call {@code String result = new MediaTokenValidatorTask().execute(resourceId, token).get()}
     *     The result will be one of:
     *     <ul>
     *         <li>{@link com.adobe.primetime.reference.manager.EntitlementManagerOn.MediaTokenValidatorTask#TVS_SUCCESS} - valid token, user granted access</li>
     *         <li>{@link com.adobe.primetime.reference.manager.EntitlementManagerOn.MediaTokenValidatorTask#TVS_ERROR} - an exception occurred, user not granted access</li>
     *         <li>any other error string - error message from TVS, user not granted access</li>
     *     </ul>
     * </p>
     */
    private static class SendEventTask extends AsyncTask<String, Void, String>
    {

        @Override
        // _endPointUrl, _authorizationString, messageBody, _useGzipEncoding ? "1" : ""
        protected String doInBackground(String... params) {

            boolean useGzip = params[3].length() > 0;
            String messageBody = params[2];
            try
            {
                HttpClient client = new DefaultHttpClient();
                HttpPost request = new HttpPost(params[0]);

                if (useGzip)
                {
                    ByteArrayOutputStream os = new ByteArrayOutputStream(messageBody.length());
                    GZIPOutputStream gos = new GZIPOutputStream(os);
                    gos.write(messageBody.getBytes());
                    os.close();
                    gos.close();
                    ByteArrayEntity gzipBody = new ByteArrayEntity(os.toByteArray());
                    request.setEntity(gzipBody);
                    request.setHeader("Content-Encoding", "gzip");
                }
                else
                {
                    StringEntity bodyEntity = new StringEntity(messageBody, "UTF-8");
                    request.setEntity(bodyEntity);
                }
                request.setHeader("Authorization", params[1]);
                PrimetimeReference.logger.i("SplunkHttpServiceConnector", messageBody);

                // @todo, handle service errors
                HttpResponse response = client.execute(request);
                BufferedReader reader =  new BufferedReader(new InputStreamReader((response.getEntity().getContent())));
                reader.close();
            }
            catch (Exception e)
            {
                // errorrrrr
                PrimetimeReference.logger.e("SplunkHttpServiceConnector", "http request failed", e);
            }

            return "OK";
        }
    }
}
