package com.streamplayer;

import android.app.Activity;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.widget.Toast;

import androidx.core.content.FileProvider;

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

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Encapsula toda la lógica para consultar releases de Gitea, descargar el APK y lanzarlo.
 */
public class UpdateManager {

    private static final String TAG = "UpdateManager";
    private static final String LATEST_RELEASE_URL =
            "https://gitea.cbcren.online/api/v1/repos/renato97/app/releases/latest";
    private static final String GITEA_TOKEN = "4b94b3610136529861af0821040a801906821a0f";

    private final Context appContext;
    private final Handler mainHandler;
    private final ExecutorService networkExecutor;
    private final OkHttpClient httpClient;

    private WeakReference<Activity> activityRef;
    private DownloadReceiver downloadReceiver;
    private long currentDownloadId = -1L;
    private File downloadingFile;
    private File pendingInstallFile;
    private UpdateInfo cachedUpdate;

    public UpdateManager(Context context) {
        this.appContext = context.getApplicationContext();
        this.mainHandler = new Handler(Looper.getMainLooper());
        this.networkExecutor = Executors.newSingleThreadExecutor();
        // Usar NetworkUtils para obtener cliente con DNS over HTTPS configurado
        this.httpClient = NetworkUtils.getClient();
    }

    public void checkForUpdates(UpdateCallback callback) {
        networkExecutor.execute(() -> {
            try {
                Request request = new Request.Builder()
                        .url(LATEST_RELEASE_URL)
                        .header("Authorization", "token " + GITEA_TOKEN)
                        .get()
                        .build();
                try (Response response = httpClient.newCall(request).execute()) {
                    if (response.body() == null) {
                        postError(callback, appContext.getString(R.string.update_error_empty_response));
                        return;
                    }
                    if (!response.isSuccessful()) {
                        postError(callback, appContext.getString(R.string.update_error_http,
                                response.code()));
                        return;
                    }
                    String body = response.body().string();
                    UpdateInfo info = parseRelease(body);
                    cachedUpdate = info;
                    if (info != null && info.isUpdateAvailable(BuildConfig.VERSION_CODE)) {
                        postAvailable(callback, info);
                    } else {
                        postUpToDate(callback);
                    }
                }
            } catch (IOException | JSONException e) {
                Log.w(TAG, "Error checking updates", e);
                postError(callback, e.getMessage());
            }
        });
    }

    public UpdateInfo getCachedUpdate() {
        return cachedUpdate;
    }

    public void downloadUpdate(Activity activity, UpdateInfo info) {
        if (info == null || TextUtils.isEmpty(info.downloadUrl)) {
            showToast(appContext.getString(R.string.update_error_missing_url));
            return;
        }
        DownloadManager downloadManager =
                (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);
        if (downloadManager == null) {
            showToast(appContext.getString(R.string.update_error_download_manager));
            return;
        }
        File targetDir = appContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
        if (targetDir == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            targetDir = appContext.getExternalFilesDir(null);
        }
        if (targetDir == null) {
            showToast(appContext.getString(R.string.update_error_storage));
            return;
        }
        if (!targetDir.exists() && !targetDir.mkdirs()) {
            showToast(appContext.getString(R.string.update_error_storage));
            return;
        }
        String fileName = info.getResolvedFileName();
        File apkFile = new File(targetDir, fileName);
        if (apkFile.exists() && !apkFile.delete()) {
            showToast(appContext.getString(R.string.update_error_storage));
            return;
        }

        Uri destination = Uri.fromFile(apkFile);
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(info.downloadUrl));
        request.setTitle(appContext.getString(R.string.update_notification_title, info.versionName));
        request.setDescription(appContext.getString(R.string.update_notification_description));
        request.setAllowedOverMetered(true);
        request.setAllowedOverRoaming(false);
        request.setNotificationVisibility(
                DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setDestinationUri(destination);

        try {
            currentDownloadId = downloadManager.enqueue(request);
        } catch (IllegalArgumentException e) {
            showToast(appContext.getString(R.string.update_error_download, e.getMessage()));
            return;
        }
        downloadingFile = apkFile;
        activityRef = new WeakReference<>(activity);
        registerDownloadReceiver();
        showToast(appContext.getString(R.string.update_download_started));
    }

    public void resumePendingInstall(Activity activity) {
        if (pendingInstallFile == null || !pendingInstallFile.exists()) {
            return;
        }
        installDownloadedApk(activity, pendingInstallFile);
    }

    public void release() {
        unregisterDownloadReceiver();
        networkExecutor.shutdownNow();
        activityRef = null;
    }

    private UpdateInfo parseRelease(String responseBody) throws JSONException, IOException {
        if (responseBody == null || responseBody.trim().isEmpty()) {
            throw new JSONException("La respuesta está vacía");
        }
        
        // Validar que no sea HTML antes de parsear
        String trimmed = responseBody.trim();
        if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
            throw new JSONException("Se recibió HTML en lugar de JSON");
        }
        
        JSONObject releaseJson = new JSONObject(responseBody);
        String tagName = releaseJson.optString("tag_name", "");
        String versionName = deriveVersionName(tagName, releaseJson.optString("name"));
        int versionCode = parseVersionCode(versionName);
        String releaseNotes = releaseJson.optString("body", "");
        String releasePageUrl = releaseJson.optString("html_url", "");
        JSONArray assets = releaseJson.optJSONArray("assets");
        JSONObject apkAsset = findApkAsset(assets);
        String downloadUrl = apkAsset != null ? apkAsset.optString("browser_download_url", "") : "";
        String downloadFileName = apkAsset != null ? apkAsset.optString("name", "") : "";
        long sizeBytes = apkAsset != null ? apkAsset.optLong("size", 0L) : 0L;
        int downloadCount = apkAsset != null ? apkAsset.optInt("download_count", 0) : 0;

        int minSupported = 0;
        boolean forceUpdate = false;
        JSONObject manifestJson = fetchManifest(assets);
        if (manifestJson != null) {
            versionCode = manifestJson.optInt("versionCode", versionCode);
            versionName = manifestJson.optString("versionName", versionName);
            minSupported = manifestJson.optInt("minSupportedVersionCode", 0);
            forceUpdate = manifestJson.optBoolean("forceUpdate", false);
            String manifestUrl = manifestJson.optString("downloadUrl", null);
            if (!TextUtils.isEmpty(manifestUrl)) {
                downloadUrl = manifestUrl;
            }
            if (manifestJson.has("fileName")) {
                downloadFileName = manifestJson.optString("fileName", downloadFileName);
            }
            if (manifestJson.has("sizeBytes")) {
                sizeBytes = manifestJson.optLong("sizeBytes", sizeBytes);
            }
            if (manifestJson.has("notes") && TextUtils.isEmpty(releaseNotes)) {
                releaseNotes = manifestJson.optString("notes", releaseNotes);
            }
        }

        if (TextUtils.isEmpty(downloadUrl)) {
            return null;
        }

        return new UpdateInfo(versionCode, versionName, releaseNotes, downloadUrl,
                downloadFileName, sizeBytes, downloadCount, releasePageUrl,
                minSupported, forceUpdate);
    }

    private JSONObject fetchManifest(JSONArray assets) throws IOException, JSONException {
        if (assets == null) {
            return null;
        }
        for (int i = 0; i < assets.length(); i++) {
            JSONObject asset = assets.optJSONObject(i);
            if (asset == null) {
                continue;
            }
            String name = asset.optString("name", "").toLowerCase(Locale.US);
            if (TextUtils.isEmpty(name) || !name.endsWith(".json")) {
                continue;
            }
            if (!(name.contains("update") || name.contains("manifest"))) {
                continue;
            }
            String url = asset.optString("browser_download_url", "");
            if (TextUtils.isEmpty(url)) {
                continue;
            }
            Request request = new Request.Builder()
                    .url(url)
                    .header("Authorization", "token " + GITEA_TOKEN)
                    .get()
                    .build();
            try (Response response = httpClient.newCall(request).execute()) {
                if (!response.isSuccessful() || response.body() == null) {
                    continue;
                }
                String json = response.body().string();
                if (!TextUtils.isEmpty(json)) {
                    // Validar que no sea HTML antes de parsear
                    String trimmed = json.trim();
                    if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
                        continue;
                    }
                    return new JSONObject(json);
                }
            }
        }
        return null;
    }

    private JSONObject findApkAsset(JSONArray assets) {
        if (assets == null) {
            return null;
        }
        JSONObject fallback = null;
        for (int i = 0; i < assets.length(); i++) {
            JSONObject asset = assets.optJSONObject(i);
            if (asset == null) {
                continue;
            }
            if (fallback == null) {
                fallback = asset;
            }
            String name = asset.optString("name", "").toLowerCase(Locale.US);
            if (name.endsWith(".apk")) {
                return asset;
            }
        }
        return fallback;
    }

    private String deriveVersionName(String tagName, String fallback) {
        String base = !TextUtils.isEmpty(tagName) ? tagName : fallback;
        if (TextUtils.isEmpty(base)) {
            return "";
        }
        return base.replaceFirst("^[Vv]", "").trim();
    }

    private int parseVersionCode(String versionName) {
        if (TextUtils.isEmpty(versionName)) {
            return -1;
        }
        String normalized = versionName.replaceAll("[^0-9\\.]", "");
        if (TextUtils.isEmpty(normalized)) {
            return -1;
        }
        String[] parts = normalized.split("\\.");
        int major = parsePart(parts, 0);
        int minor = parsePart(parts, 1);
        int patch = parsePart(parts, 2);
        return major * 10000 + minor * 100 + patch;
    }

    private int parsePart(String[] parts, int index) {
        if (parts.length <= index) {
            return 0;
        }
        try {
            return Integer.parseInt(parts[index]);
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    private void installDownloadedApk(Activity activity, File apkFile) {
        if (activity == null || apkFile == null || !apkFile.exists()) {
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            boolean canInstall = appContext.getPackageManager().canRequestPackageInstalls();
            if (!canInstall) {
                pendingInstallFile = apkFile;
                Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
                        Uri.parse("package:" + appContext.getPackageName()));
                try {
                    activity.startActivity(intent);
                } catch (ActivityNotFoundException ignored) {
                    showToast(appContext.getString(R.string.update_error_install_permissions));
                }
                showToast(appContext.getString(R.string.update_permission_request));
                return;
            }
        }
        Uri uri = FileProvider.getUriForFile(appContext,
                appContext.getPackageName() + ".fileprovider", apkFile);
        Intent installIntent = new Intent(Intent.ACTION_VIEW);
        installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
        installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            activity.startActivity(installIntent);
            pendingInstallFile = null;
        } catch (ActivityNotFoundException e) {
            showToast(appContext.getString(R.string.update_error_install_intent));
        }
    }

    private void registerDownloadReceiver() {
        if (downloadReceiver != null) {
            return;
        }
        downloadReceiver = new DownloadReceiver();
        IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        appContext.registerReceiver(downloadReceiver, filter);
    }

    private void unregisterDownloadReceiver() {
        if (downloadReceiver != null) {
            try {
                appContext.unregisterReceiver(downloadReceiver);
            } catch (IllegalArgumentException ignored) {
            }
            downloadReceiver = null;
        }
    }

    private void handleDownloadComplete(long downloadId) {
        if (downloadId != currentDownloadId) {
            return;
        }
        DownloadManager downloadManager =
                (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);
        if (downloadManager == null) {
            showToast(appContext.getString(R.string.update_error_download_manager));
            cleanupDownloadState();
            return;
        }
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(downloadId);
        Cursor cursor = downloadManager.query(query);
        if (cursor != null) {
            try {
                if (cursor.moveToFirst()) {
                    int status = cursor.getInt(cursor.getColumnIndexOrThrow(
                            DownloadManager.COLUMN_STATUS));
                    if (status == DownloadManager.STATUS_SUCCESSFUL && downloadingFile != null) {
                        pendingInstallFile = downloadingFile;
                        Activity activity = activityRef != null ? activityRef.get() : null;
                        mainHandler.post(() -> {
                            showToast(appContext.getString(R.string.update_download_complete));
                            installDownloadedApk(activity, pendingInstallFile);
                        });
                    } else {
                        int reason = cursor.getInt(cursor.getColumnIndexOrThrow(
                                DownloadManager.COLUMN_REASON));
                        mainHandler.post(() -> showToast(appContext.getString(
                                R.string.update_error_download_failed, reason)));
                    }
                }
            } finally {
                cursor.close();
            }
        }
        cleanupDownloadState();
    }

    private void cleanupDownloadState() {
        unregisterDownloadReceiver();
        currentDownloadId = -1L;
        downloadingFile = null;
    }

    private void postAvailable(UpdateCallback callback, UpdateInfo info) {
        if (callback == null) {
            return;
        }
        mainHandler.post(() -> callback.onUpdateAvailable(info));
    }

    private void postUpToDate(UpdateCallback callback) {
        if (callback == null) {
            return;
        }
        mainHandler.post(callback::onUpToDate);
    }

    private void postError(UpdateCallback callback, String message) {
        if (callback == null) {
            return;
        }
        mainHandler.post(() -> callback.onError(message));
    }

    private void showToast(String message) {
        mainHandler.post(() -> Toast.makeText(appContext, message, Toast.LENGTH_LONG).show());
    }

    private class DownloadReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L);
            handleDownloadComplete(id);
        }
    }

    public interface UpdateCallback {
        void onUpdateAvailable(UpdateInfo info);

        void onUpToDate();

        void onError(String message);
    }

    public static class UpdateInfo {
        public final int versionCode;
        public final String versionName;
        public final String releaseNotes;
        public final String downloadUrl;
        public final String downloadFileName;
        public final long downloadSizeBytes;
        public final int downloadCount;
        public final String releasePageUrl;
        public final int minSupportedVersionCode;
        public final boolean forceUpdate;

        UpdateInfo(int versionCode,
                   String versionName,
                   String releaseNotes,
                   String downloadUrl,
                   String downloadFileName,
                   long downloadSizeBytes,
                   int downloadCount,
                   String releasePageUrl,
                   int minSupportedVersionCode,
                   boolean forceUpdate) {
            this.versionCode = versionCode;
            this.versionName = versionName;
            this.releaseNotes = releaseNotes == null ? "" : releaseNotes.trim();
            this.downloadUrl = downloadUrl;
            this.downloadFileName = downloadFileName;
            this.downloadSizeBytes = downloadSizeBytes;
            this.downloadCount = downloadCount;
            this.releasePageUrl = releasePageUrl;
            this.minSupportedVersionCode = minSupportedVersionCode;
            this.forceUpdate = forceUpdate;
        }

        public boolean isUpdateAvailable(int currentVersionCode) {
            return versionCode > currentVersionCode;
        }

        public boolean isMandatory(int currentVersionCode) {
            return forceUpdate || currentVersionCode < minSupportedVersionCode;
        }

        public String getReleaseNotesPreview() {
            if (TextUtils.isEmpty(releaseNotes)) {
                return "";
            }
            final int limit = 900;
            if (releaseNotes.length() <= limit) {
                return releaseNotes;
            }
            return releaseNotes.substring(0, limit) + "\n…";
        }

        public String getResolvedFileName() {
            if (!TextUtils.isEmpty(downloadFileName)) {
                return downloadFileName;
            }
            String safeVersion = TextUtils.isEmpty(versionName) ? String.valueOf(versionCode)
                    : versionName.replaceAll("[^0-9a-zA-Z._-]", "");
            return "StreamPlayer-" + safeVersion + ".apk";
        }

        public String formatSize(Context context) {
            if (downloadSizeBytes <= 0) {
                return "";
            }
            return Formatter.formatShortFileSize(context, downloadSizeBytes);
        }
    }
}

