package com.rosetta.messenger.network

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Person
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.Icon
import android.os.Build
import android.os.IBinder
import android.util.Base64
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.IconCompat
import com.rosetta.messenger.MainActivity
import com.rosetta.messenger.R
import com.rosetta.messenger.data.PreferencesManager
import com.rosetta.messenger.database.RosettaDatabase
import com.rosetta.messenger.utils.AvatarFileManager
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

/**
 * Keeps call alive while app goes to background.
 * Uses Notification.CallStyle on Android 12+ and NotificationCompat fallback on older APIs.
 */
@AndroidEntryPoint
class CallForegroundService : Service() {

    @Inject lateinit var preferencesManager: PreferencesManager

    private data class Snapshot(
        val phase: CallPhase,
        val displayName: String,
        val statusText: String,
        val durationSec: Int,
        val peerPublicKey: String = ""
    )

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val action = intent?.action ?: ACTION_SYNC
        CallManager.initialize(applicationContext)
        val phaseNow = CallManager.state.value.phase
        notifLog("onStartCommand action=$action phase=$phaseNow")

        when (action) {
            ACTION_STOP -> {
                if (phaseNow == CallPhase.IDLE) {
                    notifLog("ACTION_STOP → stopSelf")
                    safeStopForeground()
                    return START_NOT_STICKY
                }
                // Может прилететь поздний STOP от прошлой сессии, не глушим живой звонок.
                notifLog("ACTION_STOP ignored: phase=$phaseNow")
            }
            ACTION_END -> {
                notifLog("ACTION_END → endCall")
                CallManager.endCall()
                safeStopForeground()
                return START_NOT_STICKY
            }
            ACTION_DECLINE -> {
                val phase = CallManager.state.value.phase
                notifLog("ACTION_DECLINE phase=$phase")
                if (phase == CallPhase.INCOMING) {
                    CallManager.declineIncomingCall()
                } else {
                    CallManager.endCall()
                }
                safeStopForeground()
                return START_NOT_STICKY
            }
            ACTION_ACCEPT -> {
                notifLog("ACTION_ACCEPT → acceptIncomingCall phase=${CallManager.state.value.phase}")
                // Если push пришёл раньше WebSocket — CallManager ещё в IDLE.
                // Ждём до 5 сек пока реальный CALL сигнал придёт по WebSocket.
                CoroutineScope(Dispatchers.Main).launch {
                    var accepted = false
                    for (i in 1..50) { // 50 * 100ms = 5 sec
                        val phase = CallManager.state.value.phase
                        if (phase == CallPhase.INCOMING) {
                            val result = CallManager.acceptIncomingCall()
                            notifLog("ACTION_ACCEPT attempt #$i result=$result")
                            if (result == CallActionResult.STARTED) {
                                openCallUi()
                                notifLog("ACTION_ACCEPT → openCallUi()")
                                accepted = true
                            }
                            break
                        } else if (phase != CallPhase.IDLE) {
                            notifLog("ACTION_ACCEPT phase=$phase (not INCOMING/IDLE), opening UI")
                            openCallUi()
                            accepted = true
                            break
                        }
                        delay(100)
                    }
                    if (!accepted) {
                        notifLog("ACTION_ACCEPT: timed out waiting for INCOMING, phase=${CallManager.state.value.phase}")
                    }
                }
            }
            else -> Unit
        }

        val snapshot = extractSnapshot(intent)
        notifLog("snapshot: phase=${snapshot.phase} name=${snapshot.displayName} status=${snapshot.statusText}")
        if (snapshot.phase == CallPhase.IDLE) {
            notifLog("phase=IDLE → stopSelf")
            stopForegroundCompat()
            stopSelf()
            return START_NOT_STICKY
        }

        ensureNotificationChannel()
        val notification = buildNotification(snapshot)
        val hasFullScreen = snapshot.phase == CallPhase.INCOMING
        notifLog("buildNotification OK, hasFullScreenIntent=$hasFullScreen, starting foreground")
        startForegroundCompat(notification, snapshot.phase)
        notifLog("startForeground OK, phase=${snapshot.phase}")

        // Проверяем canUseFullScreenIntent на Android 14+
        if (Build.VERSION.SDK_INT >= 34) {
            val nm = getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager
            val canFsi = nm.canUseFullScreenIntent()
            notifLog("Android 14+: canUseFullScreenIntent=$canFsi")
        }

        return START_STICKY
    }

    private fun extractSnapshot(intent: Intent?): Snapshot {
        val state = CallManager.state.value
        val payloadIntent = intent
        if (payloadIntent == null || !payloadIntent.hasExtra(EXTRA_PHASE)) {
            return Snapshot(
                phase = state.phase,
                displayName = state.displayName,
                statusText = state.statusText,
                durationSec = state.durationSec,
                peerPublicKey = state.peerPublicKey
            )
        }

        val rawPhase = payloadIntent.getStringExtra(EXTRA_PHASE).orEmpty()
        val phase = runCatching { CallPhase.valueOf(rawPhase) }.getOrElse { state.phase }
        val displayName =
            payloadIntent.getStringExtra(EXTRA_DISPLAY_NAME)
                .orEmpty()
                .ifBlank { state.displayName }
                .ifBlank { "Unknown" }
        val statusText = payloadIntent.getStringExtra(EXTRA_STATUS_TEXT).orEmpty().ifBlank { state.statusText }
        val durationSec = payloadIntent.getIntExtra(EXTRA_DURATION_SEC, state.durationSec)
        val peerPublicKey = payloadIntent.getStringExtra(EXTRA_PEER_PUBLIC_KEY)
            .orEmpty()
            .ifBlank { state.peerPublicKey }
        return Snapshot(
            phase = phase,
            displayName = displayName,
            statusText = statusText,
            durationSec = durationSec.coerceAtLeast(0),
            peerPublicKey = peerPublicKey
        )
    }

    private fun ensureNotificationChannel() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (manager.getNotificationChannel(CHANNEL_ID) != null) return

        val channel =
            NotificationChannel(
                CHANNEL_ID,
                "Calls",
                NotificationManager.IMPORTANCE_MAX
            ).apply {
                description = "Incoming and ongoing calls"
                lockscreenVisibility = Notification.VISIBILITY_PUBLIC
                setShowBadge(false)
                enableVibration(true)
                vibrationPattern = longArrayOf(0, 1000, 500, 1000)
                setBypassDnd(true)
            }
        manager.createNotificationChannel(channel)
    }

    private fun buildNotification(snapshot: Snapshot): Notification {
        // При INCOMING — нажатие открывает IncomingCallActivity (полноэкранный звонок)
        // При остальных фазах — открывает MainActivity
        val contentActivity = if (snapshot.phase == CallPhase.INCOMING) {
            com.rosetta.messenger.IncomingCallActivity::class.java
        } else {
            MainActivity::class.java
        }
        val openAppPendingIntent = PendingIntent.getActivity(
            this,
            REQUEST_OPEN_APP,
            Intent(this, contentActivity).apply {
                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
                if (contentActivity == MainActivity::class.java) {
                    putExtra(EXTRA_OPEN_CALL_FROM_NOTIFICATION, true)
                }
            },
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        val endCallPendingIntent = PendingIntent.getService(
            this,
            REQUEST_END_CALL,
            Intent(this, CallForegroundService::class.java).setAction(ACTION_END),
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        val declinePendingIntent = PendingIntent.getService(
            this,
            REQUEST_DECLINE_CALL,
            Intent(this, CallForegroundService::class.java).setAction(ACTION_DECLINE),
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        val answerPendingIntent = PendingIntent.getService(
            this,
            REQUEST_ACCEPT_CALL,
            Intent(this, CallForegroundService::class.java).setAction(ACTION_ACCEPT),
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        // fullScreenIntent открывает лёгкую IncomingCallActivity поверх lock screen
        val fullScreenPendingIntent = if (snapshot.phase == CallPhase.INCOMING) {
            PendingIntent.getActivity(
                this,
                REQUEST_FULL_SCREEN,
                Intent(this, com.rosetta.messenger.IncomingCallActivity::class.java).apply {
                    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
                },
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )
        } else null

        val defaultStatus =
            when (snapshot.phase) {
                CallPhase.INCOMING -> "Incoming call"
                CallPhase.OUTGOING -> "Calling"
                CallPhase.CONNECTING -> "Connecting"
                CallPhase.ACTIVE -> "Call in progress"
                CallPhase.IDLE -> "Call ended"
            }
        val contentText = snapshot.statusText.ifBlank { defaultStatus }
        val avatarBitmap = loadAvatarBitmap(snapshot.peerPublicKey)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            val personBuilder = Person.Builder().setName(snapshot.displayName).setImportant(true)
            if (avatarBitmap != null) {
                personBuilder.setIcon(Icon.createWithBitmap(avatarBitmap))
            }
            val person = personBuilder.build()
            val style =
                if (snapshot.phase == CallPhase.INCOMING) {
                    Notification.CallStyle.forIncomingCall(
                        person,
                        declinePendingIntent,
                        answerPendingIntent
                    )
                } else {
                    Notification.CallStyle.forOngoingCall(person, endCallPendingIntent)
                }

            return Notification.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_notification)
                .setContentTitle(snapshot.displayName)
                .setContentText(contentText)
                .setContentIntent(openAppPendingIntent)
                .setOngoing(true)
                .setCategory(Notification.CATEGORY_CALL)
                .setVisibility(Notification.VISIBILITY_PUBLIC)
                .setStyle(style)
                .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
                .apply {
                    if (fullScreenPendingIntent != null) {
                        setFullScreenIntent(fullScreenPendingIntent, true)
                    }
                }
                .apply {
                    if (snapshot.phase == CallPhase.ACTIVE) {
                        setUsesChronometer(true)
                        setWhen(System.currentTimeMillis() - snapshot.durationSec * 1000L)
                    }
                }
                .build()
        }

        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(snapshot.displayName)
            .apply { if (avatarBitmap != null) setLargeIcon(avatarBitmap) }
            .setContentText(contentText)
            .setContentIntent(openAppPendingIntent)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setCategory(NotificationCompat.CATEGORY_CALL)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setOnlyAlertOnce(true)
            .setOngoing(true)
            .apply {
                if (fullScreenPendingIntent != null) {
                    setFullScreenIntent(fullScreenPendingIntent, true)
                }
            }
            .apply {
                if (snapshot.phase == CallPhase.INCOMING) {
                    addAction(android.R.drawable.ic_menu_call, "Answer", answerPendingIntent)
                    addAction(android.R.drawable.ic_menu_close_clear_cancel, "Decline", declinePendingIntent)
                } else {
                    addAction(android.R.drawable.ic_menu_close_clear_cancel, "End", endCallPendingIntent)
                }
            }
            .apply {
                if (snapshot.phase == CallPhase.ACTIVE) {
                    setUsesChronometer(true)
                    setWhen(System.currentTimeMillis() - snapshot.durationSec * 1000L)
                }
            }
            .build()
    }

    private fun startForegroundCompat(notification: Notification, phase: CallPhase) {
        val started =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                val preferredType =
                    when (phase) {
                        CallPhase.INCOMING -> ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
                        CallPhase.OUTGOING,
                        CallPhase.CONNECTING,
                        CallPhase.ACTIVE -> ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE or ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
                        CallPhase.IDLE -> ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
                    }
                startForegroundTyped(notification, preferredType) ||
                    startForegroundTyped(notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) ||
                    startForegroundUntyped(notification)
            } else {
                startForegroundUntyped(notification)
            }

        if (!started) {
            Log.e(TAG, "Failed to start foreground service safely; stopping service")
            stopSelf()
        }
    }

    private fun startForegroundTyped(notification: Notification, type: Int): Boolean {
        return try {
            startForeground(NOTIFICATION_ID, notification, type)
            notifLog("startForeground OK type=$type")
            true
        } catch (error: Throwable) {
            notifLog("startForeground FAILED type=$type: ${error.message}")
            Log.w(TAG, "Typed startForeground failed (type=$type): ${error.message}")
            false
        }
    }

    private fun startForegroundUntyped(notification: Notification): Boolean {
        return try {
            startForeground(NOTIFICATION_ID, notification)
            notifLog("startForeground (untyped) OK")
            true
        } catch (error: Throwable) {
            notifLog("startForeground (untyped) FAILED: ${error.message}")
            Log.w(TAG, "Untyped startForeground failed: ${error.message}")
            false
        }
    }

    @Suppress("DEPRECATION")
    private fun stopForegroundCompat() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            stopForeground(STOP_FOREGROUND_REMOVE)
        } else {
            stopForeground(true)
        }
    }

    /** Безопасная остановка: startForeground → stopForeground → stopSelf.
     *  Предотвращает ForegroundServiceDidNotStartInTimeException. */
    private fun safeStopForeground() {
        ensureNotificationChannel()
        try {
            startForeground(NOTIFICATION_ID, buildPlaceholderNotification())
        } catch (_: Throwable) {}
        stopForegroundCompat()
        stopSelf()
    }

    private fun buildPlaceholderNotification(): Notification {
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle("Rosetta")
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .setSilent(true)
            .build()
    }

    companion object {
        private const val TAG = "CallForegroundService"
        private const val CHANNEL_ID = "rosetta_calls"
        private const val NOTIFICATION_ID = 9010
        private const val REQUEST_OPEN_APP = 9011
        private const val REQUEST_END_CALL = 9012
        private const val REQUEST_DECLINE_CALL = 9013
        private const val REQUEST_ACCEPT_CALL = 9014
        private const val REQUEST_FULL_SCREEN = 9015
        private const val NOTIF_LOG_FILE = "call_notification_log.txt"

        private const val ACTION_SYNC = "com.rosetta.messenger.call.ACTION_SYNC"
        private const val ACTION_END = "com.rosetta.messenger.call.ACTION_END"
        private const val ACTION_DECLINE = "com.rosetta.messenger.call.ACTION_DECLINE"
        private const val ACTION_ACCEPT = "com.rosetta.messenger.call.ACTION_ACCEPT"
        private const val ACTION_STOP = "com.rosetta.messenger.call.ACTION_STOP"

        private const val EXTRA_PHASE = "extra_phase"
        private const val EXTRA_DISPLAY_NAME = "extra_display_name"
        private const val EXTRA_STATUS_TEXT = "extra_status_text"
        private const val EXTRA_DURATION_SEC = "extra_duration_sec"
        private const val EXTRA_PEER_PUBLIC_KEY = "extra_peer_public_key"
        const val EXTRA_OPEN_CALL_FROM_NOTIFICATION = "extra_open_call_from_notification"

        fun syncWithCallState(context: Context, state: CallUiState) {
            val appContext = context.applicationContext
            if (state.phase == CallPhase.IDLE) {
                // Используем ACTION_STOP вместо stopService — он вызовет safeStopForeground
                val stopIntent = Intent(appContext, CallForegroundService::class.java).setAction(ACTION_STOP)
                runCatching { appContext.startService(stopIntent) }
                    .onFailure { appContext.stopService(Intent(appContext, CallForegroundService::class.java)) }
                return
            }

            val intent =
                Intent(appContext, CallForegroundService::class.java)
                    .setAction(ACTION_SYNC)
                    .putExtra(EXTRA_PHASE, state.phase.name)
                    .putExtra(EXTRA_DISPLAY_NAME, state.displayName)
                    .putExtra(EXTRA_STATUS_TEXT, state.statusText)
                    .putExtra(EXTRA_DURATION_SEC, state.durationSec)
                    .putExtra(EXTRA_PEER_PUBLIC_KEY, state.peerPublicKey)

            runCatching { ContextCompat.startForegroundService(appContext, intent) }
                .onFailure { error ->
                    Log.w(TAG, "Failed to start foreground service: ${error.message}")
                }
        }

        fun stop(context: Context) {
            val appContext = context.applicationContext
            val intent = Intent(appContext, CallForegroundService::class.java).setAction(ACTION_STOP)
            runCatching { appContext.startService(intent) }
                .onFailure {
                    appContext.stopService(Intent(appContext, CallForegroundService::class.java))
                }
        }
    }

    private fun loadAvatarBitmap(publicKey: String): Bitmap? {
        if (publicKey.isBlank()) return null
        // Проверяем настройку
        val avatarEnabled = runCatching {
            runBlocking(Dispatchers.IO) {
                preferencesManager.notificationAvatarEnabled.first()
            }
        }.getOrDefault(true)
        if (!avatarEnabled) return null
        return runCatching {
            val db = RosettaDatabase.getDatabase(applicationContext)
            val entity = runBlocking(Dispatchers.IO) {
                db.avatarDao().getLatestAvatarByKeys(listOf(publicKey))
            } ?: return null
            val rawBase64 = AvatarFileManager.readAvatar(applicationContext, entity.avatar)
                ?: return null
            val base64 = if (rawBase64.contains(",")) rawBase64.substringAfter(",") else rawBase64
            val bytes = Base64.decode(base64, Base64.DEFAULT)
            val original = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) ?: return null
            toCircleBitmap(original)
        }.getOrNull()
    }

    private fun toCircleBitmap(source: Bitmap): Bitmap {
        val size = minOf(source.width, source.height)
        val output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
        val canvas = android.graphics.Canvas(output)
        val paint = android.graphics.Paint().apply { isAntiAlias = true }
        val rect = android.graphics.Rect(0, 0, size, size)
        canvas.drawARGB(0, 0, 0, 0)
        canvas.drawCircle(size / 2f, size / 2f, size / 2f, paint)
        paint.xfermode = android.graphics.PorterDuffXfermode(android.graphics.PorterDuff.Mode.SRC_IN)
        canvas.drawBitmap(source, rect, rect, paint)
        return output
    }

    private fun openCallUi() {
        notifLog("openCallUi → MainActivity")
        val intent =
            Intent(this, MainActivity::class.java).apply {
                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
                putExtra(EXTRA_OPEN_CALL_FROM_NOTIFICATION, true)
            }
        runCatching { startActivity(intent) }
            .onSuccess { notifLog("openCallUi → started OK") }
            .onFailure { error ->
                notifLog("openCallUi FAILED: ${error.message}")
                Log.w(TAG, "Failed to open call UI: ${error.message}")
            }
    }

    private fun openIncomingCallUi() {
        notifLog("openIncomingCallUi → IncomingCallActivity")
        val intent =
            Intent(this, com.rosetta.messenger.IncomingCallActivity::class.java).apply {
                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
            }
        runCatching { startActivity(intent) }
            .onSuccess { notifLog("openIncomingCallUi → started OK") }
            .onFailure { error ->
                notifLog("openIncomingCallUi FAILED: ${error.message}")
                Log.w(TAG, "Failed to open incoming call UI: ${error.message}")
            }
    }

    /** Пишет лог в crash_reports/call_notification_log.txt — виден через rosettadev1 */
    private fun notifLog(msg: String) {
        Log.d(TAG, msg)
        try {
            val dir = java.io.File(applicationContext.filesDir, "crash_reports")
            if (!dir.exists()) dir.mkdirs()
            val f = java.io.File(dir, NOTIF_LOG_FILE)
            // Ограничиваем размер файла — перезаписываем если больше 100KB
            if (f.exists() && f.length() > 100_000) f.writeText("")
            val ts = java.text.SimpleDateFormat("HH:mm:ss.SSS", java.util.Locale.getDefault()).format(java.util.Date())
            f.appendText("$ts $msg\n")
        } catch (_: Throwable) {}
    }
}
