package com.goalplay.capacitormediasession;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.IBinder;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Base64;
import android.util.Log;

import androidx.core.content.ContextCompat;

import com.getcapacitor.JSArray;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.getcapacitor.annotation.Permission;
import com.getcapacitor.plugin.util.CapacitorContext;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@CapacitorPlugin(
    name = "MediaSession",
    permissions = {
        @Permission(
            alias = "foreground",
            strings = {
                "android.permission.FOREGROUND_SERVICE",
                "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"
            }
        )
    }
)
public class MediaSessionPlugin extends Plugin {
    private static final String TAG = "MediaSessionPlugin";

    private boolean startServiceOnlyDuringPlayback = true;
    private String title = "";
    private String artist = "";
    private String album = "";
    private Bitmap artwork = null;
    private String playbackState = "none";
    private double duration = 0.0;
    private double position = 0.0;
    private double playbackRate = 1.0;
    private final Map<String, PluginCall> actionHandlers = new HashMap<>();

    private MediaSessionService service = null;

    private final ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            MediaSessionService.LocalBinder binder = (MediaSessionService.LocalBinder) iBinder;
            service = binder.getService();
            Intent intent = new Intent(getContext(), getActivity().getClass());
            service.connectAndInitialize(MediaSessionPlugin.this, intent);
            updateServiceMetadata();
            updateServicePlaybackState();
            updateServicePositionState();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.d(TAG, "Disconnected from MediaSessionService");
        }
    };

    @Override
    public void load() {
        super.load();

        final String foregroundServiceConfig = getConfig().getString("foregroundService", "");
        if (foregroundServiceConfig.equals("always")) {
            startServiceOnlyDuringPlayback = false;
        }

        if (!startServiceOnlyDuringPlayback) {
            startMediaService();
        }
    }

    public void startMediaService() {
        Intent intent = new Intent(getContext(), MediaSessionService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            ContextCompat.startForegroundService(getContext(), intent);
        } else {
            getContext().startService(intent);
        }
        getContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    private void updateServiceMetadata() {
        if (service == null) return;
        service.setTitle(title);
        service.setArtist(artist);
        service.setAlbum(album);
        service.setArtwork(artwork);
        service.update();
    }

    private Bitmap urlToBitmap(String url) throws IOException {
        if (url.startsWith("blob:")) {
            Log.i(TAG, "Blob URLs not supported for artwork.");
            return null;
        }

        if (url.startsWith("http")) {
            HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();
            connection.setDoInput(true);
            connection.connect();
            InputStream inputStream = connection.getInputStream();
            return BitmapFactory.decodeStream(inputStream);
        }

        int base64Index = url.indexOf(";base64,");
        if (base64Index != -1) {
            String base64Data = url.substring(base64Index + 8);
            byte[] decoded = Base64.decode(base64Data, Base64.DEFAULT);
            return BitmapFactory.decodeByteArray(decoded, 0, decoded.length);
        }

        return null;
    }

    @PluginMethod
    public void setMetadata(PluginCall call) {
        title = call.getString("title", title);
        artist = call.getString("artist", artist);
        album = call.getString("album", album);

        try {
            JSArray artworkArray = call.getArray("artwork");
            List<JSONObject> artworkList = artworkArray.toList();
            for (JSONObject artwork : artworkList) {
                String src = artwork.getString("src");
                if (src != null) {
                    this.artwork = urlToBitmap(src);
                    break;
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Error loading artwork", e);
        }

        if (service != null) updateServiceMetadata();
        call.resolve();
    }

    private void updateServicePlaybackState() {
        if (service == null) return;
        switch (playbackState) {
            case "playing":
                service.setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
                break;
            case "paused":
                service.setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
                break;
            default:
                service.setPlaybackState(PlaybackStateCompat.STATE_NONE);
        }
        service.update();
    }

    @PluginMethod
    public void setPlaybackState(PluginCall call) {
        playbackState = call.getString("playbackState", playbackState);

        boolean shouldStart = playbackState.equals("playing") || playbackState.equals("paused");

        if (startServiceOnlyDuringPlayback && service == null && shouldStart) {
            startMediaService();
        } else if (startServiceOnlyDuringPlayback && service != null && !shouldStart) {
            getContext().unbindService(serviceConnection);
            service = null;
        } else if (service != null) {
            updateServicePlaybackState();
        }

        call.resolve();
    }

    private void updateServicePositionState() {
        if (service == null) return;
        service.setDuration((long) (duration * 1000));
        service.setPosition((long) (position * 1000));
        float rate = playbackRate == 0.0 ? 1.0f : (float) playbackRate;
        service.setPlaybackSpeed(rate);
        service.update();
    }

    @PluginMethod
    public void setPositionState(PluginCall call) {
        duration = call.getDouble("duration", 0.0);
        position = call.getDouble("position", 0.0);
        playbackRate = call.getFloat("playbackRate", 1.0f);

        if (service != null) updateServicePositionState();
        call.resolve();
    }

    @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
    public void setActionHandler(PluginCall call) {
        call.setKeepAlive(true);
        actionHandlers.put(call.getString("action"), call);

        if (service != null) service.updatePossibleActions();
    }

    public boolean hasActionHandler(String action) {
        return actionHandlers.containsKey(action) && !PluginCall.CALLBACK_ID_DANGLING.equals(actionHandlers.get(action).getCallbackId());
    }

    public void actionCallback(String action) {
        actionCallback(action, new JSObject());
    }

    public void actionCallback(String action, JSObject data) {
        PluginCall call = actionHandlers.get(action);
        if (call != null && !PluginCall.CALLBACK_ID_DANGLING.equals(call.getCallbackId())) {
            data.put("action", action);
            call.resolve(data);
        } else {
            Log.d(TAG, "No handler for action " + action);
        }
    }

    @PluginMethod
    public void startService(PluginCall call) {
        try {
            Intent intent = new Intent(getContext(), MediaSessionService.class);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                ContextCompat.startForegroundService(getContext(), intent);
            } else {
                getContext().startService(intent);
            }
            call.resolve();
        } catch (Exception e) {
            Log.e(TAG, "Error starting service", e);
            call.reject("Error starting service: " + e.getMessage());
        }
    }

    @PluginMethod
    public void stopService(PluginCall call) {
        try {
            Intent intent = new Intent(getContext(), MediaSessionService.class);
            getContext().stopService(intent);
            call.resolve();
        } catch (Exception e) {
            Log.e(TAG, "Error stopping service", e);
            call.reject("Error stopping service: " + e.getMessage());
        }
    }
}
