package chat.rocket.reactnative.voip

import android.app.Activity
import chat.rocket.reactnative.BuildConfig
import android.app.KeyguardManager
import java.util.concurrent.atomic.AtomicBoolean
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.WindowManager
import android.view.View
import android.widget.ImageView
import androidx.core.content.ContextCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.FrameLayout
import android.util.Log
import android.view.ViewOutlineProvider
import com.bumptech.glide.Glide
import chat.rocket.reactnative.R
import android.graphics.Typeface
import chat.rocket.reactnative.notification.Ejson
import chat.rocket.reactnative.notification.NotificationHelper

/**
 * Full-screen Activity displayed when an incoming VoIP call arrives.
 * Shows on lock screen and handles user actions (Accept/Decline).
 *
 * Ring audio is owned by [VoipNotification]'s NotificationChannel — do not
 * play a ringtone here; a second source would double-ring on most Android versions.
 */
class IncomingCallActivity : Activity() {

    companion object {
        private const val TAG = "RocketChat.IncomingCall"
    }

    private var voipPayload: VoipPayload? = null
    private var isCallStateReceiverRegistered = false
    private val timeoutHandler = Handler(Looper.getMainLooper())
    private var timeoutRunnable: Runnable? = null
    private val acceptDeclineGuard = AtomicBoolean(false)
    private val callStateReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val payload = VoipPayload.fromBundle(intent?.extras) ?: return
            if (payload.callId != voipPayload?.callId) {
                return
            }

            clearTimeout()
            finish()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Samsung/Xiaomi fix: Request keyguard dismissal BEFORE setting content view
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
            keyguardManager.requestDismissKeyguard(this, null)
        }

        // Android 14+ fix: Must call programmatically (XML attributes ignored)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            setShowWhenLocked(true)
            setTurnScreenOn(true)
        } else {
            // Enable showing on lock screen (for older Android versions)
            @Suppress("DEPRECATION")
            window.addFlags(
                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
                WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
                WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
            )
        }
        
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

        setContentView(R.layout.activity_incoming_call)
        applyNavigationBar()
        applyButtonBackgrounds()
        applyInterFont()

        val voipPayload = VoipPayload.fromBundle(intent.extras)
        if (voipPayload == null || !voipPayload.isVoipIncomingCall()) {
            Log.e(TAG, "Invalid VoIP payload, finishing activity")
            finish()
            return
        }
        this.voipPayload = voipPayload

        if (BuildConfig.DEBUG) {
            Log.d(TAG, "IncomingCallActivity created - callId: ${voipPayload.callId}, caller: ${voipPayload.caller}")
        }

        updateUI(voipPayload)
        setupButtons(voipPayload)
        scheduleTimeout(voipPayload)
        val intentFilter = IntentFilter().apply {
            addAction(VoipNotification.ACTION_TIMEOUT)
            addAction(VoipNotification.ACTION_DISMISS)
        }
        LocalBroadcastManager.getInstance(this).registerReceiver(callStateReceiver, intentFilter)
        isCallStateReceiverRegistered = true
    }

    private fun applyNavigationBar() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        val bgColor = ContextCompat.getColor(this, R.color.incoming_call_background)
        window.navigationBarColor = bgColor
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val isDarkTheme = (resources.configuration.uiMode and android.content.res.Configuration.UI_MODE_NIGHT_MASK) ==
                android.content.res.Configuration.UI_MODE_NIGHT_YES
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isDarkTheme) {
                @Suppress("DEPRECATION")
                window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
            }
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            window.isNavigationBarContrastEnforced = false
        }
    }

    /**
     * Applies button background colors programmatically. Required on some devices (e.g. Samsung
     * lock screen) where XML @color references may not resolve correctly in full-screen intent context.
     */
    private fun applyButtonBackgrounds() {
        val cornerRadiusPx = 8 * resources.displayMetrics.density
        findViewById<FrameLayout>(R.id.btn_reject_bg)?.apply {
            background = GradientDrawable().apply {
                setColor(ContextCompat.getColor(this@IncomingCallActivity, R.color.incoming_call_reject_bg))
                cornerRadius = cornerRadiusPx
            }
        }
        findViewById<FrameLayout>(R.id.btn_accept_bg)?.apply {
            background = GradientDrawable().apply {
                setColor(ContextCompat.getColor(this@IncomingCallActivity, R.color.incoming_call_accept_bg))
                cornerRadius = cornerRadiusPx
            }
        }
    }

    private fun applyInterFont() {
        val interRegular = try {
            Typeface.createFromAsset(assets, "fonts/Inter-Regular.otf")
        } catch (e: Exception) {
            Log.e(TAG, "Failed to load Inter-Regular font", e)
            return
        }
        val interBold = try {
            Typeface.createFromAsset(assets, "fonts/Inter-Bold.otf")
        } catch (e: Exception) {
            Log.e(TAG, "Failed to load Inter-Bold font", e)
            interRegular
        }
        listOf(
            R.id.header_text,
            R.id.host_name,
            R.id.incoming_call_reject_label,
            R.id.incoming_call_accept_label
        ).forEach { id ->
            findViewById<TextView>(id)?.setTypeface(interRegular)
        }
        findViewById<TextView>(R.id.caller_name)?.setTypeface(interBold)
    }

    private fun updateUI(payload: VoipPayload) {
        findViewById<TextView>(R.id.caller_name)?.text = payload.caller.ifEmpty { getString(R.string.incoming_call_unknown_caller) }
        findViewById<TextView>(R.id.host_name)?.text = payload.hostName.ifEmpty { getString(R.string.incoming_call_unknown_host) }

        loadAvatar(payload)
    }

    private fun loadAvatar(payload: VoipPayload) {
        val container = findViewById<FrameLayout>(R.id.avatar_container)
        val imageView = findViewById<ImageView>(R.id.avatar)
        val sizePx = (120 * resources.displayMetrics.density).toInt().coerceIn(120, 480)
        val ejson = Ejson.forCallerAvatar(payload.host, payload.username) ?: return
        val avatarUrl = ejson.getCallerAvatarUri(sizePx) ?: return
        val loadModel = NotificationHelper.avatarLoadModel(avatarUrl, ejson)
        val cornerRadiusPx = (8 * resources.displayMetrics.density).toFloat()

        Glide.with(this)
            .load(loadModel)
            .into(object : com.bumptech.glide.request.target.CustomTarget<android.graphics.drawable.Drawable>(sizePx, sizePx) {
                override fun onResourceReady(
                    resource: android.graphics.drawable.Drawable,
                    transition: com.bumptech.glide.request.transition.Transition<in android.graphics.drawable.Drawable>?
                ) {
                    container.visibility = View.VISIBLE
                    imageView.setImageDrawable(resource)
                    applyAvatarRoundCorners(imageView, cornerRadiusPx)
                }

                override fun onLoadFailed(errorDrawable: android.graphics.drawable.Drawable?) {
                    container.visibility = View.GONE
                }

                override fun onLoadCleared(placeholder: android.graphics.drawable.Drawable?) {
                    container.visibility = View.GONE
                }
            })
    }

    /**
     * Applies rounded corners via view-level clipping.
     * Works for both PNG (BitmapDrawable) and SVG (vector/PictureDrawable) since
     * Glide's RoundedCorners bitmap transform only applies to bitmaps.
     */
    private fun applyAvatarRoundCorners(imageView: ImageView, cornerRadiusPx: Float) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return
        imageView.post {
            val radius = cornerRadiusPx
            imageView.outlineProvider = object : ViewOutlineProvider() {
                override fun getOutline(view: View, outline: android.graphics.Outline) {
                    outline.setRoundRect(0, 0, view.width, view.height, radius)
                }
            }
            imageView.clipToOutline = true
        }
    }

    private fun setupButtons(payload: VoipPayload) {
        findViewById<LinearLayout>(R.id.btn_accept)?.setOnClickListener {
            handleAccept(payload)
        }

        findViewById<LinearLayout>(R.id.btn_decline)?.setOnClickListener {
            handleDecline(payload)
        }
    }

    private fun scheduleTimeout(payload: VoipPayload) {
        val remainingLifetimeMs = payload.getRemainingLifetimeMs()
        if (remainingLifetimeMs == null || remainingLifetimeMs <= 0L) {
            finish()
            return
        }

        clearTimeout()
        timeoutRunnable = Runnable {
            VoipNotification.handleTimeout(this, payload)
            finish()
        }.also { timeoutHandler.postDelayed(it, remainingLifetimeMs) }
    }

    private fun clearTimeout() {
        timeoutRunnable?.let(timeoutHandler::removeCallbacks)
        timeoutRunnable = null
    }

    private fun handleAccept(payload: VoipPayload) {
        if (!acceptDeclineGuard.compareAndSet(false, true)) return
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "Call accepted - callId: ${payload.callId}")
        }
        clearTimeout()
        VoipNotification.cancelTimeout(payload.callId)
        VoipNotification.handleAcceptAction(this, payload)
        // Activity finishes when ACTION_DISMISS is broadcast from handleAcceptAction (async DDP).
    }

    private fun handleDecline(payload: VoipPayload) {
        if (!acceptDeclineGuard.compareAndSet(false, true)) return
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "Call declined - callId: ${payload.callId}")
        }
        clearTimeout()
        VoipNotification.cancelTimeout(payload.callId)
        VoipNotification.handleDeclineAction(this, payload)

        finish()
    }

    override fun onDestroy() {
        super.onDestroy()
        clearTimeout()
        if (isCallStateReceiverRegistered) {
            LocalBroadcastManager.getInstance(this).unregisterReceiver(callStateReceiver)
            isCallStateReceiverRegistered = false
        }
    }

    override fun onBackPressed() {
        voipPayload?.let { handleDecline(it) } ?: finish()
    }
}
