/*
 * Copyright 2016 Sam Sun <me@samczsun.com>
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.samczsun.skype4j.internal.client;

import com.eclipsesource.json.JsonObject;
import com.samczsun.skype4j.chat.GroupChat;
import com.samczsun.skype4j.exceptions.ConnectionException;
import com.samczsun.skype4j.exceptions.InvalidCredentialsException;
import com.samczsun.skype4j.exceptions.NotParticipatingException;
import com.samczsun.skype4j.exceptions.handler.ErrorHandler;
import com.samczsun.skype4j.internal.Endpoints;
import com.samczsun.skype4j.internal.SkypeImpl;
import com.samczsun.skype4j.internal.Utils;
import com.samczsun.skype4j.internal.utils.UncheckedRunnable;
import com.samczsun.skype4j.participants.info.Contact;

import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class GuestClient extends SkypeImpl {
    private final String chatId;
    private final String requestedUsername;

    private volatile String actualUsername;

    public GuestClient(String username, String chatId, Set<String> resources, Logger logger, List<ErrorHandler> errorHandlers) {
        super(username, resources, logger, errorHandlers);
        this.chatId = chatId;
        this.requestedUsername = username;
    }

    @Override
    public void login() throws ConnectionException, InvalidCredentialsException {
        JsonObject response = Endpoints.NEW_GUEST
                .open(this)
                .as(JsonObject.class)
                .on(303, connection -> {
                    throw new NotParticipatingException();
                })
                .expect(201, "While logging in")
                .header("csrf_token", "skype4j")
                .cookie("csrf_token", "skype4j")
                .post(new JsonObject()
                        .add("name", requestedUsername)
                        .add("threadId", chatId)
                        .add("shortId", "Skype4J")
                        .add("flowId", "Skype4J"));
        this.setSkypeToken(response.get("skypetoken").asString());

        String[] splits = response.get("skypetoken").asString().split("\\.");
        try {
            String decoded = new String(DatatypeConverter.parseBase64Binary(Utils.makeValidBase64(splits[1])), "UTF-8");
            JsonObject object = JsonObject.readFrom(decoded).asObject();
            this.actualUsername = object.get("skypeid").asString();
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }

        List<UncheckedRunnable> tasks = new ArrayList<>();
        tasks.add(() -> {
            HttpURLConnection asmResponse = getAsmToken();
            String[] setCookie = asmResponse.getHeaderField("Set-Cookie").split(";")[0].split("=");
            this.cookies.put(setCookie[0], setCookie[1]);
        });
        tasks.add(this::registerEndpoint);

        try {
            ExecutorService executorService = Executors.newFixedThreadPool(2);
            tasks.forEach(executorService::submit);
            executorService.shutdown();
            executorService.awaitTermination(1, TimeUnit.DAYS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        super.login();
    }

    @Override
    public void logout() throws ConnectionException {
        Endpoints.LEAVE_GUEST
                .open(this, this.chatId)
                .expect(200, "While logging out")
                .cookie("guest_token_" + this.chatId, "skype4j::" + this.getSkypeToken())
                .cookie("csrf_token", "skype4j")
                .cookie("launcher_session_id", "Skype4J")
                .get();
        shutdown();
    }

    @Override
    public GroupChat createGroupChat(Contact... contacts) {
        throw new UnsupportedOperationException("Not supported with a guest account");
    }

    @Override
    public void loadAllContacts() {
        throw new UnsupportedOperationException("Not supported with a guest account");
    }

    @Override
    public void getContactRequests(boolean fromWebsocket) {
        throw new UnsupportedOperationException("Not supported with a guest account");
    }

    @Override
    public void updateContactList() {
        throw new UnsupportedOperationException("Not supported with a guest account");
    }

    @Override
    public String getUsername() {
        if (actualUsername == null) {
            throw new IllegalStateException("Should not be called when login has not completed");
        }
        return actualUsername;
    }
}
