package chat.vojo.app;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import androidx.core.content.ContextCompat;

import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;

import java.util.HashMap;
import java.util.Map;

/**
 * JS → Android bridge for CallForegroundService lifecycle.
 *
 * start / stop map onto startForegroundService / stopService.
 *
 * RECORD_AUDIO permission is re-verified here before dispatch: the JS caller
 * (useAndroidCallForegroundSync) gates on the widget's JoinCall signal, which
 * implies getUserMedia has run and the grant is in place — but the plugin
 * checks defensively so the service never attempts startForeground with
 * TYPE_MICROPHONE without the permission. The manifest declares the service
 * as foregroundServiceType="microphone" only (no fallback type), so on API
 * 34+ starting without RECORD_AUDIO would throw ForegroundServiceTypeException.
 */
@CapacitorPlugin(name = "CallForegroundService")
public class CallForegroundPlugin extends Plugin {

    private static final String TAG = "CallFgsPlugin";

    @PluginMethod
    public void start(PluginCall call) {
        String title = call.getString("title");
        String body = call.getString("body");
        Context ctx = getContext();

        // Defense-in-depth: starting the microphone-typed FGS without
        // RECORD_AUDIO granted is invalid on API 34+. JS side already gates
        // on JoinCall (see useAndroidCallForegroundSync) so this should never
        // fire in practice. If it does, resolve cleanly without starting —
        // the call will run without retention, which is the same fate as
        // the first-ever-call window before getUserMedia prompt answered.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            int micPerm = ContextCompat.checkSelfPermission(ctx, Manifest.permission.RECORD_AUDIO);
            if (micPerm != PackageManager.PERMISSION_GRANTED) {
                Log.w(TAG, "start: RECORD_AUDIO not granted, skipping FGS (would fail on TYPE_MICROPHONE)");
                call.resolve();
                return;
            }
        }

        Intent intent = new Intent(ctx, CallForegroundService.class);
        if (title != null) intent.putExtra(CallForegroundService.EXTRA_TITLE, title);
        if (body != null) intent.putExtra(CallForegroundService.EXTRA_BODY, body);
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                ctx.startForegroundService(intent);
            } else {
                ctx.startService(intent);
            }
            Log.d(TAG, "start: service started");
            call.resolve();
        } catch (Throwable t) {
            Log.e(TAG, "start: failed to start service", t);
            call.reject("start_failed: " + t.getClass().getSimpleName() + ": " + t.getMessage());
        }
    }

    @PluginMethod
    public void stop(PluginCall call) {
        Context ctx = getContext();
        try {
            ctx.stopService(new Intent(ctx, CallForegroundService.class));
            Log.d(TAG, "stop: stopService dispatched");
            call.resolve();
        } catch (Throwable t) {
            Log.w(TAG, "stop: stopService threw", t);
            call.reject("stop_failed: " + t.getClass().getSimpleName() + ": " + t.getMessage());
        }
    }

    // JS upserts a live incoming ring into the native registry (atom ADD
    // happy-path). Idempotent with any prior FCM seed for the same eventId —
    // Java merges metadata fields append-only. See VojoFirebaseMessagingService
    // for the registry operations contract.
    @PluginMethod
    public void upsertIncomingRing(PluginCall call) {
        String eventId = call.getString("eventId");
        String roomId = call.getString("roomId");
        if (eventId == null || eventId.isEmpty() || roomId == null || roomId.isEmpty()) {
            call.reject("missing_eventId_or_roomId");
            return;
        }
        Map<String, String> data = new HashMap<>();
        data.put("event_id", eventId);
        data.put("room_id", roomId);
        String callerName = call.getString("callerName");
        if (callerName != null && !callerName.isEmpty()) {
            data.put("sender_display_name", callerName);
        }
        // Pass through senderTs/lifetime as strings — registry stores the same
        // Map<String,String> shape that FCM delivers, and downstream consumers
        // (scheduleCallNotificationExpiry, isExpired) parseLong them.
        Long senderTs = call.getLong("senderTs");
        if (senderTs != null && senderTs > 0) {
            data.put("content_sender_ts", Long.toString(senderTs));
        }
        Long lifetime = call.getLong("lifetime");
        if (lifetime != null && lifetime > 0) {
            data.put("content_lifetime", Long.toString(lifetime));
        }
        String messageId = call.getString("messageId");
        // messageId is used as google.message_id in the Answer/Launch PendingIntent
        // extras — Capacitor PushNotificationsPlugin gates pushNotificationActionPerformed
        // on containsKey. Empty string also satisfies the gate; we pass the
        // caller's value through verbatim.
        boolean seeded = VojoFirebaseMessagingService.upsertIncomingRing(data, messageId);
        // Mark in NotificationDedup so a polling fire 15 minutes later
        // doesn't post a "Missed call" notification for a ring the user
        // already saw live via the in-app strip. Mirrors the FCM-arrival
        // path in VojoFirebaseMessagingService.onMessageReceived.
        if (seeded) {
            NotificationDedup.markNotified(getContext(), eventId);
        }
        call.resolve();
    }

    // JS removes a ring from the native registry (atom REMOVE / suppress path /
    // native action receiver path). Tombstones the eventId to reject late
    // FCM or /sync re-seeds within the ring lifetime.
    @PluginMethod
    public void removeIncomingRing(PluginCall call) {
        String eventId = call.getString("eventId");
        if (eventId == null || eventId.isEmpty()) {
            call.reject("missing_event_id");
            return;
        }
        VojoFirebaseMessagingService.removeIncomingRing(getContext(), eventId);
        call.resolve();
    }
}
