package app.fedilab.fedilabtube.client;
/* Copyright 2020 Thomas Schneider
 *
 * This file is a part of TubeLab
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * TubeLab 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 General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with TubeLab; if not,
 * see <http://www.gnu.org/licenses>. */

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.os.Looper;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import app.fedilab.fedilabtube.BuildConfig;
import app.fedilab.fedilabtube.MainActivity;
import app.fedilab.fedilabtube.R;
import app.fedilab.fedilabtube.client.data.AccountData;
import app.fedilab.fedilabtube.client.data.BlockData;
import app.fedilab.fedilabtube.client.data.CaptionData;
import app.fedilab.fedilabtube.client.data.ChannelData;
import app.fedilab.fedilabtube.client.data.CommentData;
import app.fedilab.fedilabtube.client.data.InstanceData;
import app.fedilab.fedilabtube.client.data.NotificationData;
import app.fedilab.fedilabtube.client.data.PlaylistData;
import app.fedilab.fedilabtube.client.data.VideoData;
import app.fedilab.fedilabtube.client.data.VideoPlaylistData;
import app.fedilab.fedilabtube.client.entities.AccountCreation;
import app.fedilab.fedilabtube.client.entities.ChannelParams;
import app.fedilab.fedilabtube.client.entities.Error;
import app.fedilab.fedilabtube.client.entities.InstanceParams;
import app.fedilab.fedilabtube.client.entities.Oauth;
import app.fedilab.fedilabtube.client.entities.OauthParams;
import app.fedilab.fedilabtube.client.entities.OverviewVideo;
import app.fedilab.fedilabtube.client.entities.PeertubeInformation;
import app.fedilab.fedilabtube.client.entities.PlaylistExist;
import app.fedilab.fedilabtube.client.entities.PlaylistParams;
import app.fedilab.fedilabtube.client.entities.Rating;
import app.fedilab.fedilabtube.client.entities.Report;
import app.fedilab.fedilabtube.client.entities.Token;
import app.fedilab.fedilabtube.client.entities.UserMe;
import app.fedilab.fedilabtube.client.entities.VideoParams;
import app.fedilab.fedilabtube.client.entities.WellKnownNodeinfo;
import app.fedilab.fedilabtube.helper.Helper;
import app.fedilab.fedilabtube.sqlite.AccountDAO;
import app.fedilab.fedilabtube.sqlite.Sqlite;
import app.fedilab.fedilabtube.viewmodel.ChannelsVM;
import app.fedilab.fedilabtube.viewmodel.CommentVM;
import app.fedilab.fedilabtube.viewmodel.PlaylistsVM;
import app.fedilab.fedilabtube.viewmodel.TimelineVM;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

@SuppressWarnings({"unused", "RedundantSuppression"})
public class RetrofitPeertubeAPI {

    private String finalUrl;
    private Context _context;
    private String instance;
    private String token;
    private Set<String> selection;

    public RetrofitPeertubeAPI(Context context) {
        _context = context;
        instance = Helper.getLiveInstance(context);
        finalUrl = "https://" + Helper.getLiveInstance(context) + "/api/v1/";
    }

    public RetrofitPeertubeAPI(Context context, String instance, String token) {
        _context = context;
        this.instance = instance;
        this.token = token;
        finalUrl = "https://" + instance + "/api/v1/";
    }

    public static void updateCredential(Activity activity, String token, String client_id, String client_secret, String refresh_token, String host) {
        new Thread(() -> {
            AccountData.Account account;
            String instance;
            if (host.startsWith("tube") || BuildConfig.full_instances) {
                instance = host;
            } else {
                instance = Helper.getPeertubeUrl(host);
            }
            try {
                UserMe userMe = new RetrofitPeertubeAPI(activity, instance, token).verifyCredentials();
                account = userMe.getAccount();
            } catch (Error error) {
                Error.displayError(activity, error);
                error.printStackTrace();
                return;
            }
            try {
                //At the state the instance can be encoded
                instance = URLDecoder.decode(instance, "utf-8");
            } catch (UnsupportedEncodingException ignored) {
            }
            SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
            account.setToken(token);
            account.setClient_id(client_id);
            account.setClient_secret(client_secret);
            account.setRefresh_token(refresh_token);
            account.setHost(instance);
            SQLiteDatabase db = Sqlite.getInstance(activity.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
            boolean userExists = new AccountDAO(activity, db).userExist(account);
            SharedPreferences.Editor editor = sharedpreferences.edit();
            editor.putString(Helper.PREF_KEY_ID, account.getId());
            editor.putString(Helper.PREF_KEY_NAME, account.getUsername());
            if (!host.startsWith("tube")) {
                editor.putString(Helper.PREF_INSTANCE, host);
            }
            editor.apply();
            if (userExists)
                new AccountDAO(activity, db).updateAccountCredential(account);
            else {
                if (account.getUsername() != null && account.getCreatedAt() != null)
                    new AccountDAO(activity, db).insertAccount(account);
            }
            Handler mainHandler = new Handler(Looper.getMainLooper());
            Runnable myRunnable = () -> {
                Intent mainActivity = new Intent(activity, MainActivity.class);
                mainActivity.putExtra(Helper.INTENT_ACTION, Helper.ADD_USER_INTENT);
                activity.startActivity(mainActivity);
                activity.finish();
            };
            mainHandler.post(myRunnable);
        }).start();
    }

    private String getToken() {
        return "Bearer " + token;
    }

    private PeertubeService init() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(finalUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        if (token == null) {
            token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
        }
        selection = sharedpreferences.getStringSet(_context.getString(R.string.set_video_language_choice), null);
        return retrofit.create(PeertubeService.class);
    }

    private PeertubeService initTranslation() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://" + instance)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        return retrofit.create(PeertubeService.class);
    }

    /***
     * Verifiy credential of the authenticated user *synchronously*
     * @return Account
     */
    public Token manageToken(OauthParams oauthParams) throws Error {
        PeertubeService peertubeService = init();
        Call<Token> refreshTokenCall = null;
        if (oauthParams.getGrant_type().compareTo("password") == 0) {
            refreshTokenCall = peertubeService.createToken(oauthParams.getClient_id(), oauthParams.getClient_secret(), oauthParams.getGrant_type(), oauthParams.getUsername(), oauthParams.getPassword());
        } else if (oauthParams.getGrant_type().compareTo("refresh_token") == 0) {
            refreshTokenCall = peertubeService.refreshToken(oauthParams.getClient_id(), oauthParams.getClient_secret(), oauthParams.getRefresh_token(), oauthParams.getGrant_type());
        }
        if (refreshTokenCall != null) {
            try {
                Response<Token> response = refreshTokenCall.execute();
                if (response.isSuccessful()) {
                    Token tokenReply = response.body();
                    if (oauthParams.getGrant_type().compareTo("refresh_token") == 0 && tokenReply != null) {
                        SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
                        SharedPreferences.Editor editor = sharedpreferences.edit();
                        editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, tokenReply.getAccess_token());
                        editor.apply();
                        SQLiteDatabase db = Sqlite.getInstance(_context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
                        new AccountDAO(_context, db).updateAccountToken(tokenReply);
                    }
                    return tokenReply;
                } else {
                    Error error = new Error();
                    error.setStatusCode(response.code());
                    if (response.errorBody() != null) {
                        error.setError(response.errorBody().string());
                    } else {
                        error.setError(_context.getString(R.string.toast_error));
                    }
                    throw error;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * Retrieve notifications
     *
     * @param max_id   String pagination
     * @param since_id String pagination
     * @return APIResponse
     */
    public APIResponse getNotifications(String max_id, String since_id) {
        APIResponse apiResponse = new APIResponse();
        PeertubeService peertubeService = init();
        Call<NotificationData> notificationsCall = peertubeService.getNotifications("Bearer " + token, max_id, since_id);
        try {
            Response<NotificationData> response = notificationsCall.execute();
            if (response.isSuccessful() && response.body() != null) {
                apiResponse.setPeertubeNotifications(response.body().data);
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Get caption content
     *
     * @param path String path to caption
     * @return APIResponse
     */
    public APIResponse getCaptionContent(String path) {
        APIResponse apiResponse = new APIResponse();
        PeertubeService peertubeService = init();
        Call<String> captionContentCall = peertubeService.getCaptionContent(path);
        try {
            Response<String> response = captionContentCall.execute();
            if (response.isSuccessful()) {
                apiResponse.setCaptionText(response.body());
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Get videos in a channel
     *
     * @param channelId String id of the channel
     * @param max_id    String pagination
     * @return APIResponse
     */
    public APIResponse getVideosForChannel(String channelId, String max_id) {
        APIResponse apiResponse = new APIResponse();
        PeertubeService peertubeService = init();
        Call<VideoData> videoCall = peertubeService.getChannelVideos(channelId, max_id);
        if (videoCall != null) {
            try {
                Response<VideoData> response = videoCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setPeertubes(response.body().data);
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } catch (IOException e) {
                Error error = new Error();
                error.setError(_context.getString(R.string.toast_error));
                apiResponse.setError(error);
                e.printStackTrace();
            }
        }
        return apiResponse;
    }

    public APIResponse getTL(TimelineVM.TimelineType timelineType, String max_id) {
        APIResponse apiResponse = new APIResponse();
        PeertubeService peertubeService = init();
        Call<VideoData> videoCall = null;
        ArrayList<String> filter = selection != null ? new ArrayList<>(selection) : null;
        switch (timelineType) {
            case MY_VIDEOS:
                videoCall = peertubeService.getMyVideos(getToken(), max_id);
                break;
            case SUBSCRIBTIONS:
                videoCall = peertubeService.getSubscriptionVideos(getToken(), max_id, filter);
                break;
            case MOST_LIKED:
                videoCall = peertubeService.getMostLikedVideos(max_id, filter);
                break;
            case LOCAL:
                videoCall = peertubeService.getLocalVideos(max_id, filter);
                break;
            case TRENDING:
                videoCall = peertubeService.getTrendingVideos(max_id, filter);
                break;
            case HISTORY:
                videoCall = peertubeService.getHistory(getToken(), max_id);
                break;
            case RECENT:
                videoCall = peertubeService.getRecentlyAddedVideos(max_id, filter);
                break;
        }
        if (videoCall != null) {
            try {
                Response<VideoData> response = videoCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setPeertubes(response.body().data);
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } catch (IOException e) {
                Error error = new Error();
                error.setError(_context.getString(R.string.toast_error));
                apiResponse.setError(error);
                e.printStackTrace();
            }
        }
        return apiResponse;
    }

    /**
     * Retrieves overview videos *synchronously*
     *
     * @param page String id pagination
     * @return APIResponse
     */
    public APIResponse getOverviewVideo(String page) {
        APIResponse apiResponse = new APIResponse();
        PeertubeService peertubeService = init();
        ArrayList<String> filter = selection != null ? new ArrayList<>(selection) : null;
        Call<OverviewVideo> overviewVideoCall = peertubeService.getOverviewVideos(page, filter);
        try {
            Response<OverviewVideo> response = overviewVideoCall.execute();
            if (response.isSuccessful() && response.body() != null) {
                apiResponse.setOverviewVideo(response.body());
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }


    /**
     * Check if users via their uris are following the authenticated user
     *
     * @param uris List<String>
     * @return APIResponse
     */
    public APIResponse isFollowing(String uris) {
        APIResponse apiResponse = new APIResponse();
        PeertubeService peertubeService = init();
        Call<Map<String, Boolean>> followingCall = peertubeService.getSubscriptionExists(getToken(), uris);
        try {
            Response<Map<String, Boolean>> response = followingCall.execute();
            if (response.isSuccessful()) {
                List<Map<String, Boolean>> relations = new ArrayList<>();
                relations.add(response.body());
                apiResponse.setRelationships(relations);
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Check if users via their uris are following the authenticated user
     *
     * @param uris List<String>
     * @return APIResponse
     */
    public APIResponse areFollowing(List<String> uris) {
        APIResponse apiResponse = new APIResponse();
        PeertubeService peertubeService = init();
        Call<List<Map<String, Boolean>>> followingCall = peertubeService.getSubscriptionsExist(getToken(), uris);
        try {
            Response<List<Map<String, Boolean>>> response = followingCall.execute();
            if (response.isSuccessful()) {
                apiResponse.setRelationships(response.body());
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Find captions for a video
     *
     * @param videoId String id of the video
     * @return APIResponse
     */
    public APIResponse getCaptions(String videoId) {

        APIResponse apiResponse = new APIResponse();
        PeertubeService peertubeService = init();
        Call<CaptionData> captions = peertubeService.getCaptions(videoId);
        try {
            Response<CaptionData> response = captions.execute();
            if (response.isSuccessful() && response.body() != null) {
                apiResponse.setCaptions(response.body().data);

            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Returns informations about Peertube such privacies, licenses, etc.
     *
     * @return PeertubeInformation information about peertube
     */
    public PeertubeInformation getPeertubeInformation() {
        PeertubeInformation peertubeInformation = new PeertubeInformation();
        PeertubeService peertubeService = init();
        Call<Map<Integer, String>> categories = peertubeService.getCategories();
        try {
            Response<Map<Integer, String>> response = categories.execute();
            if (response.isSuccessful()) {
                peertubeInformation.setCategories(response.body());
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Call<Map<String, String>> languages = peertubeService.getLanguages();
        try {
            Response<Map<String, String>> response = languages.execute();
            if (response.isSuccessful()) {
                peertubeInformation.setLanguages(response.body());
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Call<Map<Integer, String>> privacies = peertubeService.getPrivacies();
        try {
            Response<Map<Integer, String>> response = privacies.execute();
            if (response.isSuccessful()) {
                peertubeInformation.setPrivacies(response.body());
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Call<Map<Integer, String>> playlistsPrivacies = peertubeService.getPlaylistsPrivacies();
        try {
            Response<Map<Integer, String>> response = playlistsPrivacies.execute();
            if (response.isSuccessful()) {
                peertubeInformation.setPlaylistPrivacies(response.body());
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Call<Map<Integer, String>> licenses = peertubeService.getLicences();
        try {
            Response<Map<Integer, String>> response = licenses.execute();
            if (response.isSuccessful()) {
                peertubeInformation.setLicences(response.body());
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String lang = null;
        if (PeertubeInformation.langueMapped.containsKey(Locale.getDefault().getLanguage()))
            lang = PeertubeInformation.langueMapped.get(Locale.getDefault().getLanguage());

        Call<Map<String, String>> translations = initTranslation().getTranslations(lang);
        try {
            Response<Map<String, String>> response = translations.execute();
            if (response.isSuccessful()) {
                peertubeInformation.setTranslations(response.body());
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return peertubeInformation;
    }

    /**
     * Get instances
     *
     * @param instanceParams InstanceParams
     * @return APIResponse
     */
    public APIResponse getInstances(InstanceParams instanceParams) {
        PeertubeService peertubeService = init();
        LinkedHashMap<String, String> params = new LinkedHashMap<>();
        params.put("start", "0");
        params.put("count", "250");
        params.put("healthy", "true");
        params.put("signup", "true");
        params.put("minUserQuota", instanceParams.getMinUserQuota());
        Call<InstanceData> instancesCall = peertubeService.getInstances(params, instanceParams.getNsfwPolicy(), instanceParams.getCategoriesOr(), instanceParams.getLanguagesOr());
        APIResponse apiResponse = new APIResponse();
        try {
            Response<InstanceData> response = instancesCall.execute();
            if (!response.isSuccessful()) {
                setError(apiResponse, response.code(), response.errorBody());
            } else {
                InstanceData instanceData = response.body();
                if (instanceData != null) {
                    apiResponse.setInstances(instanceData.data);
                }
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Retrieves peertube search *synchronously*
     *
     * @param query String search
     * @return APIResponse
     */
    public APIResponse searchPeertube(String query, String max_id) {
        PeertubeService peertubeService = init();
        Call<VideoData> searchVideosCall = peertubeService.searchVideos(query, max_id);
        APIResponse apiResponse = new APIResponse();
        try {
            Response<VideoData> response = searchVideosCall.execute();
            if (response.isSuccessful() && response.body() != null) {
                apiResponse.setPeertubes(response.body().data);
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /***
     * Verifiy credential of the authenticated user *synchronously*
     * @return Account
     */
    public UserMe verifyCredentials() throws Error {
        PeertubeService peertubeService = init();
        Call<UserMe> accountCall = peertubeService.verifyCredentials("Bearer " + token);
        APIResponse apiResponse = new APIResponse();
        try {
            Response<UserMe> response = accountCall.execute();
            if (response.isSuccessful() && response.body() != null) {
                return response.body();
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
                throw error;
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return null;
    }

    public APIResponse report(Report report) {
        PeertubeService peertubeService = init();
        Call<String> report1 = peertubeService.report(getToken(), report);
        APIResponse apiResponse = new APIResponse();
        try {
            Response<String> response = report1.execute();
            if (response.isSuccessful()) {
                apiResponse.setActionReturn(response.body());
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /***
     * Update a video
     * @param videoId String id of the video
     * @param videoParams VideoParams params for the video
     * @param thumbnail File thumbnail
     * @param previewfile File preview
     * @return APIResponse
     */
    public APIResponse updateVideo(String videoId, VideoParams videoParams, File thumbnail, File previewfile) {
        PeertubeService peertubeService = init();
        MultipartBody.Part bodyThumbnail = null;
        MultipartBody.Part bodyPreviewfile = null;
        if (thumbnail != null) {
            RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), thumbnail);
            bodyThumbnail = MultipartBody.Part.createFormData("image", thumbnail.getName(), requestFile);
        }
        if (previewfile != null) {
            RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), previewfile);
            bodyPreviewfile = MultipartBody.Part.createFormData("image", previewfile.getName(), requestFile);
        }
        RequestBody channelId = RequestBody.create(MediaType.parse("text/plain"), videoParams.getChannelId());
        RequestBody description = RequestBody.create(MediaType.parse("text/plain"), videoParams.getDescription());
        RequestBody language = RequestBody.create(MediaType.parse("text/plain"), videoParams.getLanguage());
        RequestBody license = RequestBody.create(MediaType.parse("text/plain"), videoParams.getLicence());
        RequestBody name = RequestBody.create(MediaType.parse("text/plain"), videoParams.getName());

        List<RequestBody> tags = null;
        if (videoParams.getTags() != null && videoParams.getTags().size() > 0) {
            tags = new ArrayList<>();
            for (String tag : videoParams.getTags()) {
                tags.add(RequestBody.create(MediaType.parse("text/plain"), tag));
            }
        }
        RequestBody support = null;
        if (videoParams.getSupport() != null) {
            support = RequestBody.create(MediaType.parse("text/plain"), videoParams.getSupport());
        }


        Call<String> upload = peertubeService.updateVideo(getToken(), videoId,
                channelId, name, videoParams.getCategory(), videoParams.isCommentsEnabled(), description, videoParams.isDownloadEnabled(), language, license, videoParams.isNsfw(),
                videoParams.getPrivacy(), support, tags, videoParams.isWaitTranscoding()
                , bodyThumbnail, bodyPreviewfile);
        APIResponse apiResponse = new APIResponse();
        try {
            Response<String> response = upload.execute();
            if (response.isSuccessful()) {
                apiResponse.setActionReturn("ok");
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    public APIResponse createAccount(AccountCreation accountCreation) {
        PeertubeService peertubeService = init();
        Call<String> report1 = peertubeService.register(accountCreation.getEmail(), accountCreation.getPassword(), accountCreation.getUsername(), accountCreation.getDisplayName());
        APIResponse apiResponse = new APIResponse();
        try {
            Response<String> response = report1.execute();
            if (response.isSuccessful()) {
                apiResponse.setActionReturn(accountCreation.getEmail());
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    public APIResponse post(ActionType actionType, String id, String element) {
        PeertubeService peertubeService = init();
        Call<String> postCall = null;
        APIResponse apiResponse = new APIResponse();
        switch (actionType) {
            case FOLLOW:
                postCall = peertubeService.follow(getToken(), id);
                break;
            case UNFOLLOW:
                postCall = peertubeService.unfollow(getToken(), id);
                break;
            case MUTE:
                postCall = peertubeService.mute(getToken(), id);
                break;
            case UNMUTE:
                postCall = peertubeService.unmute(getToken(), id);
                break;
            case RATEVIDEO:
                postCall = peertubeService.rate(getToken(), id, element);
                break;
            case PEERTUBEDELETEVIDEO:
                postCall = peertubeService.deleteVideo(getToken(), id);
                break;
            case PEERTUBEDELETECOMMENT:
                postCall = peertubeService.deleteComment(getToken(), id, element);
                break;
            case DELETE_CHANNEL:
                postCall = peertubeService.deleteChannel(getToken(), id);
                break;
        }
        if (postCall != null) {
            try {
                Response<String> response = postCall.execute();
                if (response.isSuccessful()) {
                    apiResponse.setActionReturn(response.body());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } catch (IOException e) {
                Error error = new Error();
                error.setError(_context.getString(R.string.toast_error));
                apiResponse.setError(error);
                e.printStackTrace();
            }
        }
        return apiResponse;
    }

    /**
     * Get muted accounts
     *
     * @param maxId String (pagination)
     * @return APIResponse
     */
    public APIResponse getMuted(String maxId) {
        PeertubeService peertubeService = init();
        Call<BlockData> accountDataCall = peertubeService.getMuted("Bearer " + token, maxId);
        APIResponse apiResponse = new APIResponse();
        if (accountDataCall != null) {
            try {
                Response<BlockData> response = accountDataCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setMuted(response.body().getData());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } catch (IOException e) {
                Error error = new Error();
                error.setError(_context.getString(R.string.toast_error));
                apiResponse.setError(error);
                e.printStackTrace();
            }
        }
        return apiResponse;
    }

    /**
     * Get subscriptions data
     *
     * @param maxId String (pagination)
     * @return APIResponse
     */
    public APIResponse getSubscribtions(String maxId) {
        PeertubeService peertubeService = init();
        Call<AccountData> accountDataCall = peertubeService.getSubscription("Bearer " + token, maxId);
        APIResponse apiResponse = new APIResponse();
        if (accountDataCall != null) {
            try {
                Response<AccountData> response = accountDataCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setAccounts(response.body().data);
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } catch (IOException e) {
                Error error = new Error();
                error.setError(_context.getString(R.string.toast_error));
                apiResponse.setError(error);
                e.printStackTrace();
            }
        }
        return apiResponse;
    }

    /**
     * Create or update a channel
     *
     * @param apiAction     ChannelsVM.action
     * @param channelId     String
     * @param channelParams PlaylistParams
     * @return APIResponse
     */
    public APIResponse createOrUpdateChannel(ChannelsVM.action apiAction, String channelId, ChannelParams channelParams) {
        PeertubeService peertubeService = init();

        APIResponse apiResponse = new APIResponse();
        try {
            if (apiAction == ChannelsVM.action.CREATE_CHANNEL) {
                Call<ChannelData.ChannelCreation> stringCall = peertubeService.addChannel(getToken(), channelParams);
                Response<ChannelData.ChannelCreation> response = stringCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setActionReturn(response.body().getVideoChannel().getId());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }

            } else if (apiAction == ChannelsVM.action.UPDATE_CHANNEL) {
                Call<String> stringCall = peertubeService.updateChannel(getToken(), channelId, channelParams);
                Response<String> response = stringCall.execute();
                if (response.isSuccessful()) {
                    apiResponse.setActionReturn(response.body());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Get Oauth
     *
     * @return APIResponse
     */
    public Oauth oauthClient(String client_name, String redirect_uris, String scopes, String website) {
        PeertubeService peertubeService = init();
        try {
            Call<Oauth> oauth;
            if (BuildConfig.full_instances) {
                oauth = peertubeService.getOauth(client_name, redirect_uris, scopes, website);
            } else {
                oauth = peertubeService.getOauthAcad();
            }
            Response<Oauth> response = oauth.execute();
            if (response.isSuccessful() && response.body() != null) {
                return response.body();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Get NodeInfo
     *
     * @return APIResponse
     */
    public WellKnownNodeinfo.NodeInfo getNodeInfo() {
        PeertubeService peertubeService = init();
        try {
            Call<WellKnownNodeinfo> wellKnownNodeinfoCall = peertubeService.getWellKnownNodeinfo();
            Response<WellKnownNodeinfo> response = wellKnownNodeinfoCall.execute();
            if (response.isSuccessful() && response.body() != null) {
                if (response.body().getHref() != null) {
                    Call<WellKnownNodeinfo.NodeInfo> nodeinfo = peertubeService.getNodeinfo(response.body().getHref());
                    Response<WellKnownNodeinfo.NodeInfo> responseNodeInfo = nodeinfo.execute();
                    return responseNodeInfo.body();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Get channel data
     *
     * @param accountDataType AccountDataType (type of requested data)
     * @param element         String (pagination or name for the channel)
     * @return APIResponse
     */
    public APIResponse getChannelData(DataType accountDataType, String element) {
        PeertubeService peertubeService = init();

        APIResponse apiResponse = new APIResponse();
        switch (accountDataType) {
            case MY_CHANNELS:
            case CHANNELS_FOR_ACCOUNT:
                Call<ChannelData> channelDataCall = peertubeService.getChannelsForAccount(element);
                try {
                    Response<ChannelData> response = channelDataCall.execute();
                    if (response.isSuccessful() && response.body() != null) {

                        apiResponse.setChannels(response.body().data);
                    } else {
                        setError(apiResponse, response.code(), response.errorBody());
                    }
                } catch (IOException e) {
                    Error error = new Error();
                    error.setError(_context.getString(R.string.toast_error));
                    apiResponse.setError(error);
                    e.printStackTrace();
                }
                break;
            case CHANNEL:
                Call<ChannelData.Channel> channelCall = peertubeService.getChannel(element);
                try {
                    Response<ChannelData.Channel> response = channelCall.execute();
                    if (!response.isSuccessful()) {
                        setError(apiResponse, response.code(), response.errorBody());
                    } else {
                        ChannelData.Channel channelData = response.body();
                        if (channelData != null) {
                            List<ChannelData.Channel> channelList = new ArrayList<>();
                            channelList.add(channelData);
                            apiResponse.setChannels(channelList);
                        }
                    }
                } catch (IOException e) {
                    Error error = new Error();
                    error.setError(_context.getString(R.string.toast_error));
                    apiResponse.setError(error);
                    e.printStackTrace();
                }
                break;
        }
        return apiResponse;
    }

    /**
     * Create or update a playlist
     *
     * @param apiAction      PlaylistsVM.action
     * @param playlistId     String
     * @param playlistParams PlaylistParams
     * @return APIResponse
     */
    public APIResponse createOrUpdatePlaylist(PlaylistsVM.action apiAction, String playlistId, PlaylistParams playlistParams, File thumbnail) {
        PeertubeService peertubeService = init();

        APIResponse apiResponse = new APIResponse();
        MultipartBody.Part body = null;
        if (thumbnail != null) {
            RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), thumbnail);
            body = MultipartBody.Part.createFormData("image", thumbnail.getName(), requestFile);
        }
        try {
            RequestBody displayName = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getDisplayName());
            RequestBody description = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getDescription());
            RequestBody channelId = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getVideoChannelId());
            if (apiAction == PlaylistsVM.action.CREATE_PLAYLIST) {
                Call<VideoPlaylistData.VideoPlaylistCreation> stringCall = peertubeService.addPlaylist(getToken(), displayName, description, playlistParams.getPrivacy(), channelId, body);
                Response<VideoPlaylistData.VideoPlaylistCreation> response = stringCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setActionReturn(response.body().getVideoPlaylist().getId());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }

            } else if (apiAction == PlaylistsVM.action.UPDATE_PLAYLIST) {
                Call<String> stringCall = peertubeService.updatePlaylist(getToken(), playlistId, displayName, description, playlistParams.getPrivacy(), channelId, body);
                Response<String> response = stringCall.execute();
                if (response.isSuccessful()) {
                    apiResponse.setActionReturn(response.body());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }


    /**
     * Retrieves playlists for a video *synchronously*
     *
     * @param videoIds List<String> ids of videos
     * @return APIResponse
     */
    public APIResponse getVideosExist(String videoIds) {
        PeertubeService peertubeService = init();
        APIResponse apiResponse = new APIResponse();
        try {
            Call<Map<String, List<PlaylistExist>>> videoExistsInPlaylist = peertubeService.getVideoExistsInPlaylist(getToken(), videoIds);
            Response<Map<String, List<PlaylistExist>>> response = videoExistsInPlaylist.execute();
            if (response.isSuccessful() && response.body() != null) {
                apiResponse.setVideoExistPlaylist(response.body());
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }


    /**
     * Retrieves playlist  *synchronously*
     *
     * @param type       PlaylistsVM.action
     * @param playlistId String id of the playlist
     * @param videoId    String id of the video
     * @return APIResponse
     */
    public APIResponse playlistAction(PlaylistsVM.action type, String playlistId, String videoId, String acct) {
        PeertubeService peertubeService = init();

        APIResponse apiResponse = new APIResponse();
        try {

            if (type == PlaylistsVM.action.GET_PLAYLIST_INFO) {
                Call<PlaylistData.Playlist> playlistCall = peertubeService.getPlaylist(playlistId);
                Response<PlaylistData.Playlist> response = playlistCall.execute();
                if (response.isSuccessful()) {
                    List<PlaylistData.Playlist> playlists = new ArrayList<>();
                    playlists.add(response.body());
                    apiResponse.setPlaylists(playlists);
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }

            } else if (type == PlaylistsVM.action.GET_PLAYLISTS) {
                Call<PlaylistData> playlistsCall = peertubeService.getPlaylistsForAccount(getToken(), acct);
                Response<PlaylistData> response = playlistsCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setPlaylists(response.body().data);
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } else if (type == PlaylistsVM.action.GET_LIST_VIDEOS) {
                Call<VideoPlaylistData> videosPlayList = peertubeService.getVideosPlayList(getToken(), playlistId);
                Response<VideoPlaylistData> response = videosPlayList.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setVideoPlaylist(response.body().data);
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } else if (type == PlaylistsVM.action.DELETE_PLAYLIST) {
                Call<String> stringCall = peertubeService.deletePlaylist(getToken(), playlistId);
                Response<String> response = stringCall.execute();
                if (response.isSuccessful()) {
                    apiResponse.setActionReturn(response.body());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } else if (type == PlaylistsVM.action.ADD_VIDEOS) {
                Call<VideoPlaylistData.PlaylistElement> stringCall = peertubeService.addVideoInPlaylist(getToken(), playlistId, videoId);
                Response<VideoPlaylistData.PlaylistElement> response = stringCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setActionReturn(response.body().getVideoPlaylistElement().getId());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } else if (type == PlaylistsVM.action.DELETE_VIDEOS) {
                Call<String> stringCall = peertubeService.deleteVideoInPlaylist(getToken(), playlistId, videoId);
                Response<String> response = stringCall.execute();
                if (response.isSuccessful()) {
                    apiResponse.setActionReturn(response.body());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            }

        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    private void setError(APIResponse apiResponse, int responseCode, ResponseBody errorBody) {
        Error error;
        if (errorBody != null) {
            try {
                error = generateError(responseCode, errorBody.string());
            } catch (IOException e) {
                error = new Error();
                error.setStatusCode(responseCode);
                error.setError(_context.getString(R.string.toast_error));
            }
        } else {
            error = new Error();
            error.setStatusCode(responseCode);
            error.setError(_context.getString(R.string.toast_error));
        }
        apiResponse.setError(error);
    }

    public APIResponse getComments(CommentVM.action type, String videoId, String forCommentId, String max_id) {
        PeertubeService peertubeService = init();

        APIResponse apiResponse = new APIResponse();
        try {
            if (type == CommentVM.action.GET_THREAD) {
                Call<CommentData> commentsCall = peertubeService.getComments(videoId, max_id);
                Response<CommentData> response = commentsCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setComments(response.body().data);
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } else if (type == CommentVM.action.GET_REPLIES) {
                Call<CommentData> commentsCall = peertubeService.getReplies(videoId, forCommentId);
                Response<CommentData> response = commentsCall.execute();
                if (response.isSuccessful() && response.body() != null) {
                    apiResponse.setComments(response.body().data);
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Manage comments  *synchronously*
     *
     * @param type        (CommentVM.action
     * @param videoId     String id of the video
     * @param toCommentId String id of the comment for replies
     * @param text        String text
     * @return APIResponse
     */
    public APIResponse commentAction(RetrofitPeertubeAPI.ActionType type, String videoId, String toCommentId, String text) {
        PeertubeService peertubeService = init();

        APIResponse apiResponse = new APIResponse();
        try {
            if (type == ActionType.ADD_COMMENT) {
                Call<String> stringCall = peertubeService.postComment(getToken(), videoId, text);
                Response<String> response = stringCall.execute();
                if (response.isSuccessful()) {
                    apiResponse.setActionReturn(response.body());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            } else if (type == ActionType.REPLY) {
                Call<String> stringCall = peertubeService.postReply(getToken(), videoId, toCommentId, text);
                Response<String> response = stringCall.execute();
                if (response.isSuccessful()) {
                    apiResponse.setActionReturn(response.body());
                } else {
                    setError(apiResponse, response.code(), response.errorBody());
                }
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Retrieves playlist  *synchronously*
     *
     * @param id String id
     * @return APIResponse
     */
    public APIResponse getPlayist(String id) {
        PeertubeService peertubeService = init();
        Call<PlaylistData.Playlist> playlistCall;
        playlistCall = peertubeService.getPlaylist(id);
        APIResponse apiResponse = new APIResponse();
        try {
            Response<PlaylistData.Playlist> response = playlistCall.execute();
            if (response.isSuccessful()) {
                List<PlaylistData.Playlist> playlists = new ArrayList<>();
                playlists.add(response.body());
                apiResponse.setPlaylists(playlists);
            } else {
                setError(apiResponse, response.code(), response.errorBody());
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    private Error generateError(int responseCode, String message) {
        Error error = new Error();
        error.setStatusCode(responseCode);
        if (message != null) {
            error.setError(message);
        } else {
            error.setError(_context.getString(R.string.toast_error));
        }
        return error;
    }

    /**
     * Retrieves rating of user on a video  *synchronously*
     *
     * @param id String id
     * @return APIResponse
     */
    public APIResponse getRating(String id) {
        PeertubeService peertubeService = init();
        Call<Rating> rating = peertubeService.getRating(getToken(), id);
        APIResponse apiResponse = new APIResponse();
        try {
            Response<Rating> response = rating.execute();
            if (response.isSuccessful()) {
                apiResponse.setRating(response.body());
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Retrieves videos  *synchronously*
     *
     * @param id String id
     * @return APIResponse
     */
    public APIResponse getVideos(String id, boolean myVideo) {
        PeertubeService peertubeService = init();
        Call<VideoData.Video> video;
        if (myVideo) {
            video = peertubeService.getMyVideo(getToken(), id);
        } else {
            video = peertubeService.getVideo(id);
        }
        APIResponse apiResponse = new APIResponse();
        try {
            Response<VideoData.Video> response = video.execute();
            if (response.isSuccessful()) {
                List<VideoData.Video> videos = new ArrayList<>();
                videos.add(response.body());
                apiResponse.setPeertubes(videos);
            } else {
                Error error = new Error();
                error.setStatusCode(response.code());
                if (response.errorBody() != null) {
                    error.setError(response.errorBody().string());
                } else {
                    error.setError(_context.getString(R.string.toast_error));
                }
            }
        } catch (IOException e) {
            Error error = new Error();
            error.setError(_context.getString(R.string.toast_error));
            apiResponse.setError(error);
            e.printStackTrace();
        }
        return apiResponse;

    }


    public enum DataType {
        SUBSCRIBER,
        MUTED,
        CHANNELS_FOR_ACCOUNT,
        CHANNEL,
        MY_CHANNELS
    }


    public enum ActionType {
        FOLLOW,
        UNFOLLOW,
        MUTE,
        UNMUTE,
        RATEVIDEO,
        PEERTUBEDELETECOMMENT,
        PEERTUBEDELETEVIDEO,
        REPORT_VIDEO,
        REPORT_ACCOUNT,
        REPORT_COMMENT,
        DELETE_CHANNEL,
        ADD_COMMENT,
        REPLY,
    }


}
