package env;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONObject;

import cartago.Artifact;
import cartago.OPERATION;

public class TwitterArtifact extends Artifact {

    private static final String USER_TWEETS_URL =
            "https://api.twitterapi.io/twitter/user/last_tweets";
    private static final String TWEET_REPLIES_URL_V2 =
            "https://api.twitterapi.io/twitter/tweet/replies/v2";
    /* You need to create an API_KEY at: https://twitterapi.io/dashboard */
    private static final String API_KEY = "NONE";

    private static Cache cache = null;

    void init() {
        System.out.println("TwitterArtifact inicializado");
    }

    @OPERATION
    public void collectTweets(String user) {
        Result result = perceive(user);

        if (result.tweet == null) {
            System.out.println("⚠️ Nenhum tweet com replies válidas foi encontrado para " + user);
            signal("replies_done");
            return;
        }

        signal("tweet",
                result.tweet.id,
                result.tweet.text,
                result.tweet.author,
                result.tweet.createdAt,
                result.tweet.likes
        );

        for (int i = 0; i < result.replies.length; i++) {
            Tweet reply = result.replies[i];
            signal("reply",
                    i + 1,
                    reply.text,
                    reply.author,
                    reply.createdAt,
                    reply.likes
            );
        }

        signal("replies_done");
    }

    private Result perceive(String username) {
        if (cache != null && cache.username.equals(username)) {
            System.out.println("Usando cache para " + username);
            return cache.data;
        }

        try {
            JSONObject tweetResponse = doGetJson(
                    USER_TWEETS_URL + "?userName=" + encode(username) + "&limit=3"
            );

            JSONObject dataObj = tweetResponse.optJSONObject("data");
            JSONArray tweets = dataObj != null ? dataObj.optJSONArray("tweets") : null;

            if (tweets == null || tweets.length() == 0) {
                return new Result(null, new Tweet[0]);
            }

            for (int i = 0; i < tweets.length(); i++) {
                JSONObject t = tweets.getJSONObject(i);

                Tweet tweet = new Tweet(
                        t.optString("id", ""),
                        cleanTweetText(t.optString("text", "")),
                        extractTweetAuthor(t, username),
                        t.optString("createdAt", ""),
                        t.optInt("likeCount", 0)
                );

                sleep(4500);

                JSONObject repliesResponse = doGetJson(
                        TWEET_REPLIES_URL_V2
                                + "?tweetId=" + encode(tweet.id)
                                + "&cursor="
                                + "&queryType=Latest"
                );

                JSONArray repliesRaw = repliesResponse.optJSONArray("replies");
                if (repliesRaw == null) {
                    repliesRaw = repliesResponse.optJSONArray("tweets");
                }
                if (repliesRaw == null) {
                    repliesRaw = new JSONArray();
                }

                Tweet[] replies = mapReplies(repliesRaw);

                if (replies.length > 0) {
                    Result result = new Result(tweet, replies);
                    cache = new Cache(username, result);
                    return result;
                }
            }

            return new Result(null, new Tweet[0]);

        } catch (Exception error) {
            String msg = error.getMessage() != null ? error.getMessage() : "erro desconhecido";

            if (msg.contains("429")) {
                System.out.println("Rate limit atingido");
            }

            System.out.println("Erro ao buscar dados: " + msg);
            return new Result(null, new Tweet[0]);
        }
    }

    private Tweet[] mapReplies(JSONArray repliesRaw) {
        List<Tweet> replies = new ArrayList<>();

        for (int i = 0; i < repliesRaw.length(); i++) {
            JSONObject r = repliesRaw.optJSONObject(i);
            if (r == null) continue;

            String cleanedText = cleanTweetText(r.optString("text", ""));
            if (cleanedText.isBlank() || cleanedText.length() <= 3) continue;

            replies.add(new Tweet(
                    r.optString("id", String.valueOf(i + 1)),
                    cleanedText,
                    extractReplyAuthor(r),
                    r.optString("createdAt", ""),
                    r.optInt("likeCount", 0)
            ));
        }

        return replies.toArray(new Tweet[0]);
    }

    private String extractTweetAuthor(JSONObject tweetObj, String fallbackUsername) {
        JSONObject authorObj = tweetObj.optJSONObject("author");
        if (authorObj == null) return fallbackUsername;

        String username = authorObj.optString("username", "");
        if (username.isBlank()) {
            username = authorObj.optString("userName", "");
        }

        return username.isBlank() ? fallbackUsername : username;
    }

    private String extractReplyAuthor(JSONObject replyObj) {
        JSONObject authorObj = replyObj.optJSONObject("author");
        if (authorObj == null) return "unknown";

        String username = authorObj.optString("userName", "");
        if (username.isBlank()) {
            username = authorObj.optString("username", "");
        }

        return username.isBlank() ? "unknown" : username;
    }

    private JSONObject doGetJson(String urlString) throws Exception {
        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        conn.setRequestMethod("GET");
        conn.setRequestProperty("X-API-Key", API_KEY);
        conn.setRequestProperty("Accept", "application/json");
        conn.setConnectTimeout(15000);
        conn.setReadTimeout(15000);

        int status = conn.getResponseCode();
        InputStream stream = (status >= 200 && status < 300) ? conn.getInputStream() : conn.getErrorStream();
        String response = readAll(stream);

        if (status == 429) {
            throw new RuntimeException("429 Too Many Requests: " + response);
        }

        if (status < 200 || status >= 300) {
            throw new RuntimeException("HTTP " + status + ": " + response);
        }

        conn.disconnect();
        return new JSONObject(response);
    }

    private String readAll(InputStream stream) throws Exception {
        if (stream == null) return "";

        BufferedReader reader = new BufferedReader(
                new InputStreamReader(stream, StandardCharsets.UTF_8)
        );

        StringBuilder response = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            response.append(line);
        }

        reader.close();
        return response.toString();
    }

    private String cleanTweetText(String text) {
        if (text == null) return "";

        return text
                .replaceAll("http\\S+", "")
                .replaceAll("@\\w+", "")
                .replaceAll("\\s+", " ")
                .trim();
    }

    private void sleep(long ms) throws InterruptedException {
        Thread.sleep(ms);
    }

    private String encode(String value) {
        return URLEncoder.encode(value, StandardCharsets.UTF_8);
    }

    private static class Tweet {
        String id;
        String text;
        String author;
        String createdAt;
        int likes;

        Tweet(String id, String text, String author, String createdAt, int likes) {
            this.id = id;
            this.text = text;
            this.author = author;
            this.createdAt = createdAt;
            this.likes = likes;
        }
    }

    private static class Result {
        Tweet tweet;
        Tweet[] replies;

        Result(Tweet tweet, Tweet[] replies) {
            this.tweet = tweet;
            this.replies = replies;
        }
    }

    private static class Cache {
        String username;
        Result data;

        Cache(String username, Result data) {
            this.username = username;
            this.data = data;
        }
    }
}