package com.jishisongfu.duobibi.service

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.graphics.PixelFormat
import android.os.Build
import android.os.IBinder
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.jishisongfu.duobibi.MainActivity
import com.jishisongfu.duobibi.R
import com.jishisongfu.duobibi.data.repo.AlertHit
import com.jishisongfu.duobibi.data.repo.PriceRepository
import com.jishisongfu.duobibi.floating.AccessibilityServiceHolder
import com.jishisongfu.duobibi.floating.FloatingButtonView
import com.jishisongfu.duobibi.heartbeat.HeartbeatSender
import com.jishisongfu.duobibi.util.Constants
import com.jishisongfu.duobibi.util.PrivacyPrefs
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject

@AndroidEntryPoint
class FloatingWindowService : Service() {

    @Inject lateinit var repository: PriceRepository

    private lateinit var windowManager: WindowManager
    private var floatingView: FloatingButtonView? = null
    private var layoutParams: WindowManager.LayoutParams? = null
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    private var inflightJob: Job? = null

    override fun onCreate() {
        super.onCreate()
        Log.i(TAG, "[FLOAT] onCreate")
        instance = this
        startForegroundNotification()
        windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
        attachFloatingView()
        if (PrivacyPrefs.agreed(this)) {
            scope.launch { HeartbeatSender.send(applicationContext, "floating_create") }
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = START_STICKY

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

    override fun onDestroy() {
        Log.i(TAG, "[FLOAT] onDestroy")
        detachFloatingView()
        scope.cancel()
        instance = null
        super.onDestroy()
    }

    private fun startForegroundNotification() {
        val mgr = getSystemService(NotificationManager::class.java)
        val channel = NotificationChannel(
            Constants.FLOATING_NOTIFICATION_CHANNEL_ID,
            getString(R.string.floating_notification_channel),
            NotificationManager.IMPORTANCE_MIN,
        )
        mgr.createNotificationChannel(channel)

        val notification: Notification = NotificationCompat.Builder(
            this, Constants.FLOATING_NOTIFICATION_CHANNEL_ID,
        )
            .setContentTitle(getString(R.string.floating_notification_title))
            .setContentText(getString(R.string.floating_notification_text))
            .setSmallIcon(R.mipmap.ic_launcher)
            .setOngoing(true)
            .setPriority(NotificationCompat.PRIORITY_MIN)
            .build()

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            startForeground(
                Constants.FLOATING_NOTIFICATION_ID,
                notification,
                ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE,
            )
        } else {
            startForeground(Constants.FLOATING_NOTIFICATION_ID, notification)
        }
    }

    private fun attachFloatingView() {
        if (floatingView != null) return
        val sizePx = (resources.displayMetrics.density * 56).toInt()
        val params = WindowManager.LayoutParams(
            sizePx, sizePx,
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT,
        ).apply {
            gravity = Gravity.TOP or Gravity.START
            val saved = com.jishisongfu.duobibi.util.SettingsPrefs.savedPosition(this@FloatingWindowService)
            x = saved?.first ?: (resources.displayMetrics.widthPixels - sizePx - 32)
            y = saved?.second ?: (resources.displayMetrics.heightPixels / 2)
        }
        layoutParams = params
        val view = FloatingButtonView(this, windowManager, params, ::onFloatingClicked)
        floatingView = view
        try {
            windowManager.addView(view, params)
            view.visibility = View.GONE // 默认隐藏,等无障碍服务通知白名单 App 进前台再亮
            Log.i(TAG, "[FLOAT] attachFloatingView OK pos=(${params.x},${params.y}) size=$sizePx")
        } catch (e: Exception) {
            Log.e(TAG, "[FLOAT] attachFloatingView FAILED: ${e.message}", e)
            Toast.makeText(this, "浮窗启动失败:${e.message}", Toast.LENGTH_LONG).show()
            stopSelf()
        }
    }

    private fun detachFloatingView() {
        floatingView?.let { v -> try { windowManager.removeView(v) } catch (_: Exception) {} }
        floatingView = null
    }

    private fun applyVisibility(show: Boolean) {
        val v = floatingView ?: return
        v.post { v.visibility = if (show) View.VISIBLE else View.GONE }
    }

    private fun onFloatingClicked() {
        if (inflightJob?.isActive == true) return
        val pkg = AccessibilityServiceHolder.lastForegroundPackage
        if (pkg == null || pkg !in Constants.WHITELIST_PACKAGES) {
            Toast.makeText(this, R.string.toast_no_pkg, Toast.LENGTH_SHORT).show()
            return
        }
        floatingView?.setLoading(true)
        inflightJob = scope.launch {
            val result = runCatching {
                val tree = withContext(Dispatchers.Default) {
                    AccessibilityServiceHolder.get()?.captureCurrentTree()
                }
                withContext(Dispatchers.IO) { repository.parseAndInsert(pkg, tree) }
            }
            floatingView?.setLoading(false)
            result.fold(
                onSuccess = { outcome ->
                    val r = outcome.record
                    val priceText = formatPrice(r.price)
                    val typical = outcome.typicalPrice
                    val msg = when {
                        typical != null && r.price < typical ->
                            getString(R.string.toast_typical_lower, priceText, r.sourceApp, formatPrice(typical), formatPrice(typical - r.price))
                        typical != null && r.price > typical ->
                            getString(R.string.toast_typical_higher, priceText, r.sourceApp, formatPrice(typical), formatPrice(r.price - typical))
                        typical != null ->
                            getString(R.string.toast_typical_same, priceText, r.sourceApp, formatPrice(typical))
                        outcome.hitExistingCluster && outcome.clusterMinPriceBefore != null && r.price < outcome.clusterMinPriceBefore ->
                            getString(R.string.toast_record_lower, priceText, r.sourceApp, formatPrice(outcome.clusterMinPriceBefore - r.price))
                        outcome.hitExistingCluster && outcome.clusterMinPriceBefore != null && r.price > outcome.clusterMinPriceBefore ->
                            getString(R.string.toast_record_higher, priceText, r.sourceApp, formatPrice(r.price - outcome.clusterMinPriceBefore))
                        outcome.hitExistingCluster ->
                            getString(R.string.toast_record_same, priceText, r.sourceApp)
                        else ->
                            getString(R.string.toast_record_new, priceText, r.sourceApp)
                    }
                    Toast.makeText(this@FloatingWindowService, msg, Toast.LENGTH_LONG).show()
                    if (outcome.triggeredAlerts.isNotEmpty()) notifyAlertHits(outcome.triggeredAlerts)
                },
                onFailure = {
                    Toast.makeText(this@FloatingWindowService, R.string.toast_parse_failed, Toast.LENGTH_SHORT).show()
                },
            )
        }
    }

    /** 浮窗记到价命中了"想买的"降价雷达且达到心理价 → 本地通知。 */
    private fun notifyAlertHits(hits: List<AlertHit>) {
        val nm = getSystemService(NotificationManager::class.java) ?: return
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            nm.createNotificationChannel(
                NotificationChannel(
                    Constants.ALERT_NOTIFICATION_CHANNEL_ID,
                    getString(R.string.alert_notification_channel),
                    NotificationManager.IMPORTANCE_HIGH,
                )
            )
        }
        val openAppIntent = Intent(this, MainActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
        }
        val openAppPi = PendingIntent.getActivity(
            this, 0, openAppIntent,
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
        )
        hits.forEach { hit ->
            val title = if (hit.targetPrice != null && hit.reachedTarget) "🎯 降到心理价啦!" else "🔔 降价提醒"
            val targetSuffix = if (hit.targetPrice != null) "(心理价 ¥${formatPrice(hit.targetPrice)})" else ""
            val text = "${hit.title}: ¥${formatPrice(hit.newPrice)}(${hit.sourceApp})$targetSuffix"
            val notif = NotificationCompat.Builder(this, Constants.ALERT_NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(title)
                .setContentText(text)
                .setStyle(NotificationCompat.BigTextStyle().bigText(text))
                .setAutoCancel(true)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(openAppPi)
                .build()
            nm.notify(Constants.ALERT_NOTIFICATION_ID_BASE + hit.alertId.toInt(), notif)
        }
    }

    private fun formatPrice(p: Double): String =
        if (p == p.toLong().toDouble()) p.toLong().toString() else "%.2f".format(p)

    companion object {
        @Volatile
        private var instance: FloatingWindowService? = null

        fun isRunning(): Boolean = instance != null

        /** 显隐总入口;服务不在且需显示时顺手拉起(自我修复)。 */
        fun notifyVisibility(ctx: Context, show: Boolean) {
            val running = instance
            if (running != null) {
                running.applyVisibility(show)
            } else if (show) {
                ContextCompat.startForegroundService(ctx, Intent(ctx, FloatingWindowService::class.java))
            }
        }

        private const val TAG = "DUOBIBI"
    }
}
