package ctbrec.sites.chaturbate;

import ctbrec.Config;
import ctbrec.io.HtmlParser;
import ctbrec.io.HttpClient;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.NoSuchElementException;
import java.util.concurrent.Semaphore;

import static ctbrec.io.HttpConstants.REFERER;
import static ctbrec.io.HttpConstants.USER_AGENT;

@Slf4j
public class ChaturbateHttpClient extends HttpClient {

    private static final String PATH = "/auth/login/"; // NOSONAR
    protected String token;

    private static final Semaphore requestThrottle = new Semaphore(2, true);
    private static long lastRequest = 0;

    public ChaturbateHttpClient(Config config) {
        super("chaturbate", config);
    }

    private void extractCsrfToken(Request request) {
        try {
            Cookie csrfToken = cookieJar.getCookie(request.url(), "csrftoken");
            token = csrfToken.value();
        } catch (NoSuchElementException e) {
            log.trace("CSRF token not found in cookies");
        }
    }

    public String getToken() throws IOException {
        if (token == null) {
            login();
        }
        return token;
    }

    @Override
    public boolean login() throws IOException {
        if (loggedIn) {
            return true;
        }
        if (checkLogin()) {
            loggedIn = true;
            log.debug("Logged in with cookies");
            return true;
        }
        Request login = new Request.Builder()
                .url(Chaturbate.baseUrl + PATH)
                .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
                .build();
        try (var initResponse = client.newCall(login).execute()) {
            String content = initResponse.body().string();
            token = HtmlParser.getTag(content, "input[name=csrfmiddlewaretoken]").attr("value");
            log.debug("csrf token is {}", token);

            RequestBody body = new FormBody.Builder()
                    .add("username", Config.getInstance().getSettings().chaturbateUsername)
                    .add("password", Config.getInstance().getSettings().chaturbatePassword)
                    .add("next", "")
                    .add("csrfmiddlewaretoken", token)
                    .build();
            login = new Request.Builder()
                    .url(Chaturbate.baseUrl + PATH)
                    .header(REFERER, Chaturbate.baseUrl + PATH)
                    .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
                    .post(body)
                    .build();

            try (var loginResponse = client.newCall(login).execute()) {
                if (loginResponse.isSuccessful()) {
                    content = loginResponse.body().string();
                    if (content.contains("Login, Chaturbate login")) {
                        loggedIn = false;
                    } else {
                        loggedIn = true;
                        extractCsrfToken(login);
                    }
                }
            }
        } catch (Exception ex) {
            log.debug("Login failed: {}", ex.getMessage());
        }
        return loggedIn;
    }

    public boolean checkLogin() {
        String url = "https://chaturbate.com/api/ts/chatmessages/pm_users/?offset=0";
        Request req = new Request.Builder()
                .url(url)
                .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
                .build();
        try (Response response = execute(req)) {
            boolean result = false;
            if (response.isSuccessful()) {
                String content = response.body().string();
                if (content.startsWith("[")) {
                    result = true;
                }
            }
            log.trace("Chaturbate client login result: {}, {}", result, response.body().string());
            return result;
        } catch (Exception ex) {
            return false;
        }
    }

    @Override
    public Response execute(Request req) throws IOException {
        boolean throttled = req.url().host().contains("chaturbate.com");
        return executeThrottled(req, throttled);
    }

    private Response executeThrottled(Request req, boolean throttle) throws IOException {
        try {
            if (throttle) {
                acquireSlot();
            }
            Response resp = super.execute(req);
            extractCsrfToken(req);
            return resp;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new InterruptedIOException("Interrupted during request");
        } finally {
            if (throttle) {
                releaseSlot();
            }
        }
    }

    private static void acquireSlot() throws InterruptedException {
        long pauseBetweenRequests = Config.getInstance().getSettings().chaturbateMsBetweenRequests;
        requestThrottle.acquire();
        long now = System.currentTimeMillis();
        long millisSinceLastRequest = now - lastRequest;
        if (millisSinceLastRequest < pauseBetweenRequests) {
            Thread.sleep(pauseBetweenRequests - millisSinceLastRequest);
        }
    }

    private static void releaseSlot() {
        lastRequest = System.currentTimeMillis();
        requestThrottle.release();
    }
}
