package com.seyalix.hr.ui

import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import android.widget.Toast
import com.seyalix.hr.BuildConfig
import com.seyalix.hr.R
import com.seyalix.hr.api.ApiClient
import com.seyalix.hr.ui.approvals.ApprovalsFragment
import com.seyalix.hr.ui.attendance.AttendanceFragment
import com.seyalix.hr.ui.auth.LoginActivity
import com.seyalix.hr.ui.dashboard.DashboardFragment
import com.seyalix.hr.ui.leave.LeaveFragment
import com.seyalix.hr.ui.muster.MusterFragment
import com.seyalix.hr.ui.notifications.NotificationsFragment
import com.seyalix.hr.ui.reports.ReportsFragment
import com.seyalix.hr.utils.PrefsManager
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

    private lateinit var prefs: PrefsManager

    // ── Pending-approval polling ────────────────────────────────
    // This is the piece that actually makes leave/permission/late/muster
    // requests "reach" the reporting head: previously the Approvals screen
    // only checked the server when a manager happened to open it manually
    // (and, until the matching endpoints were added server-side, the
    // permission/late tabs silently showed nothing even then). Now the app
    // polls in the background while open and raises a notification + badge
    // the moment something new is pending. No server push (FCM) needed.
    private val pollHandler = Handler(Looper.getMainLooper())
    private val pollIntervalMs = 30_000L
    private var polling = false
    private var lastKnownTotal = 0

    private val pollRunnable = object : Runnable {
        override fun run() {
            checkPendingApprovals()
            pollHandler.postDelayed(this, pollIntervalMs)
        }
    }

    // ── Personal notifications polling (FIX 10 Jul 2026) ────────
    // Same pattern as the approvals poller above, but for the personal
    // notifications bell (apply/approve/reject alerts etc) — runs for
    // EVERY role, not just approvers, since everyone gets these.
    private val notifPollHandler = Handler(Looper.getMainLooper())
    private var notifPolling = false
    private var lastKnownUnreadNotifs = 0

    private val notifPollRunnable = object : Runnable {
        override fun run() {
            checkNotifications()
            notifPollHandler.postDelayed(this, pollIntervalMs)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        prefs = PrefsManager(this)
        createNotificationChannel()
        createNotifChannel()
        requestNotificationPermissionIfNeeded()

        if (savedInstanceState == null) {
            if (intent.getBooleanExtra("open_notifications", false)) {
                loadFragment(NotificationsFragment())
            } else {
                loadFragment(DashboardFragment())
            }
        }

        findViewById<com.google.android.material.bottomnavigation.BottomNavigationView>(R.id.bottomNav)
            .setOnItemSelectedListener { item ->
                when (item.itemId) {
                    R.id.nav_dashboard -> { loadFragment(DashboardFragment()); true }
                    R.id.nav_attendance -> { loadFragment(AttendanceFragment()); true }
                    R.id.nav_leave -> { loadFragment(LeaveFragment()); true }
                    R.id.nav_muster -> { loadFragment(MusterFragment()); true }
                    R.id.nav_more -> { showMoreMenu(); false } // don't visually select "More"
                    else -> { loadFragment(DashboardFragment()); true }
                }
            }

        // Update-available popup — same idea as a phone's system update
        // popup: check once per app launch, and if the vendor has pushed
        // a newer build, show it with an "Update Now" button. Doesn't
        // block the rest of the app (this app has no forced-update
        // column yet), so it's safe even if the check fails or is slow.
        checkAppUpdate()
    }

    override fun onResume() {
        super.onResume()
        // Only reporting heads (manager/hr/admin/superadmin) ever have
        // anything to approve, so don't waste calls polling for employees.
        if (prefs.canApprove() && !polling) {
            polling = true
            pollHandler.post(pollRunnable)
        }
        // Personal notifications apply to every role.
        if (!notifPolling) {
            notifPolling = true
            notifPollHandler.post(notifPollRunnable)
        }
    }

    override fun onPause() {
        super.onPause()
        pollHandler.removeCallbacks(pollRunnable)
        polling = false
        notifPollHandler.removeCallbacks(notifPollRunnable)
        notifPolling = false
    }

    private fun checkPendingApprovals() {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.getPendingCounts()
                if (resp.isSuccessful && resp.body()?.success == true) {
                    val total = resp.body()?.data?.total ?: 0
                    lastKnownTotal = total
                    updateApprovalBadge(total)

                    val lastSeen = prefs.getLastSeenApprovalCount()
                    if (total > lastSeen) {
                        showApprovalNotification(total - lastSeen, total)
                        prefs.setLastSeenApprovalCount(total)
                    } else if (total < lastSeen) {
                        // some got approved/rejected elsewhere (web or another
                        // device) — bring the "seen" baseline back down too
                        prefs.setLastSeenApprovalCount(total)
                    }
                }
            } catch (_: Exception) {
                // Background poll — fail silently and try again next tick.
            }
        }
    }

    private fun checkNotifications() {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.getUnreadNotificationCount()
                if (resp.isSuccessful && resp.body()?.success == true) {
                    val count = resp.body()?.data?.count ?: 0
                    lastKnownUnreadNotifs = count

                    val lastSeen = prefs.getLastSeenNotifCount()
                    if (count > lastSeen) {
                        showNotificationPopup(count - lastSeen, count)
                        prefs.setLastSeenNotifCount(count)
                    } else if (count < lastSeen) {
                        // some got read elsewhere (web or another device) —
                        // bring the "seen" baseline back down too
                        prefs.setLastSeenNotifCount(count)
                    }
                }
            } catch (_: Exception) {
                // Background poll — fail silently and try again next tick.
            }
        }
    }

    private fun updateApprovalBadge(total: Int) {
        val nav = findViewById<com.google.android.material.bottomnavigation.BottomNavigationView>(R.id.bottomNav)
        if (total > 0) {
            val badge = nav.getOrCreateBadge(R.id.nav_more)
            badge.isVisible = true
            badge.number = total
        } else {
            nav.removeBadge(R.id.nav_more)
        }
    }

    private fun showApprovalNotification(newCount: Int, total: Int) {
        if (Build.VERSION.SDK_INT >= 33 &&
            ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
            != PackageManager.PERMISSION_GRANTED
        ) {
            return // no permission — the badge dot on "More" still shows it
        }
        val intent = Intent(this, MainActivity::class.java)
        val pending = PendingIntent.getActivity(
            this, 0, intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        val text = if (newCount == 1) "1 new request needs your approval"
                   else "$newCount new requests need your approval"
        val notif = NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .setContentTitle("Pending approvals ($total)")
            .setContentText(text)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .setContentIntent(pending)
            .build()
        (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
            .notify(APPROVAL_NOTIF_ID, notif)
    }

    // FIX (10 Jul 2026): popup for personal notifications — same mechanism
    // as showApprovalNotification above, separate channel/id so the two
    // don't stomp on each other, and tapping it opens NotificationsFragment
    // directly instead of just the dashboard.
    private fun showNotificationPopup(newCount: Int, total: Int) {
        if (Build.VERSION.SDK_INT >= 33 &&
            ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
            != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }
        val intent = Intent(this, MainActivity::class.java).apply {
            putExtra("open_notifications", true)
        }
        val pending = PendingIntent.getActivity(
            this, 1, intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        val text = if (newCount == 1) "You have 1 new notification"
                   else "You have $newCount new notifications"
        val notif = NotificationCompat.Builder(this, NOTIF_CHANNEL_ID)
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .setContentTitle("Notifications ($total unread)")
            .setContentText(text)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .setContentIntent(pending)
            .build()
        (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
            .notify(NOTIF_ID, notif)
    }

    private fun requestNotificationPermissionIfNeeded() {
        if (Build.VERSION.SDK_INT >= 33 &&
            ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
            != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1001)
        }
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID, "Pending Approvals", NotificationManager.IMPORTANCE_HIGH
            ).apply {
                description = "Leave, permission, late & muster requests waiting for your approval"
            }
            (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
        }
    }

    // FIX (10 Jul 2026): separate channel for personal notifications, so
    // users can mute/configure it independently from approval alerts.
    private fun createNotifChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                NOTIF_CHANNEL_ID, "Notifications", NotificationManager.IMPORTANCE_HIGH
            ).apply {
                description = "Alerts about your own leave, permission, and late-mark requests"
            }
            (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
        }
    }

    // ── App Update popup ─────────────────────────────────────────
    // Calls the public ?action=app_version endpoint (no login needed),
    // compares it against this build's versionCode, and — if the
    // vendor has pushed something newer — shows a popup with an
    // "Update Now" button that opens the APK/Play Store URL, just like
    // a phone's own "system update available" popup. "Later" just
    // dismisses it; it'll ask again next launch until they update.
    private fun checkAppUpdate() {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.getAppVersion()
                if (!resp.isSuccessful) return@launch
                val info = resp.body()?.data ?: return@launch
                if (info.latestVersionCode <= BuildConfig.VERSION_CODE) return@launch // already latest

                if (isFinishing || isDestroyed) return@launch
                AlertDialog.Builder(this@MainActivity)
                    .setTitle("Update Available — v${info.latestVersionName}")
                    .setMessage(info.message)
                    .setCancelable(true)
                    .setPositiveButton("Update Now") { _, _ ->
                        if (info.updateUrl.isNotBlank()) {
                            try {
                                startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(info.updateUrl)))
                            } catch (e: Exception) {
                                // Bad/unreachable update_url configured server-side — fail
                                // quietly, don't crash the app over a broken link.
                            }
                        }
                    }
                    .setNegativeButton("Later", null)
                    .show()
            } catch (e: Exception) {
                // Network hiccup / server not reachable — don't bother the
                // user, just skip the popup for this launch and try again
                // next time.
            }
        }
    }

    // BottomNavigationView supports a max of 5 items, so less-frequently-used
    // sections (Approvals, Reports) live behind this "More" picker instead.
    private fun showMoreMenu() {
        val options = arrayOf("Notifications", "Approvals", "Payslips", "Holiday List", "Reports", "Logout")
        AlertDialog.Builder(this)
            .setTitle("More")
            .setItems(options) { _, which ->
                when (which) {
                    0 -> openNotifications()
                    1 -> openApprovals()
                    2 -> showPayslipList()
                    3 -> showHolidayList()
                    4 -> loadFragment(ReportsFragment())
                    5 -> logout()
                }
            }
            .show()
    }

    // Payslips — mirrors showHolidayList()'s simple list-dialog pattern.
    // Tapping a month opens the full earnings/deductions breakup, with a
    // Share button (Android's own share sheet — the equivalent of the
    // "Download" button on mobile-web, since there's no PDF generator
    // bundled into this app).
    private val monthNames = arrayOf("", "January","February","March","April","May","June","July","August","September","October","November","December")

    // FIX (10 Aug 2026): when the server hits a PHP fatal error, it
    // returns an HTML error page instead of JSON. Retrofit/Gson then
    // throws a JsonSyntaxException / MalformedJsonException while
    // trying to parse it — which used to surface as a cryptic
    // "Expected BEGIN_OBJECT but was STRING..." message that looked
    // like a random crash instead of what it actually is: a server-side
    // problem (missing file, un-run migration, etc), not a network one.
    private fun friendlyError(e: Throwable): String {
        val raw = e.message ?: e.toString()
        val looksLikeBadJson = e is com.google.gson.JsonSyntaxException ||
            e is com.google.gson.stream.MalformedJsonException ||
            raw.contains("BEGIN_OBJECT", ignoreCase = true) ||
            raw.contains("BEGIN_ARRAY", ignoreCase = true) ||
            raw.contains("Unterminated", ignoreCase = true)
        return if (looksLikeBadJson)
            "Server error — the server didn't return valid data. This usually means a file wasn't uploaded correctly or a setup step (like a database migration) hasn't been run yet. Ask your admin to check."
        else
            raw
    }

    private fun showPayslipList() {
        lifecycleScope.launch {
            try {
                val resp = com.seyalix.hr.api.ApiClient.apiService.getPayslipList()
                val list = if (resp.isSuccessful && resp.body()?.success == true)
                    resp.body()!!.data?.payslips ?: emptyList() else emptyList()

                if (list.isEmpty()) {
                    AlertDialog.Builder(this@MainActivity)
                        .setTitle("Payslips")
                        .setMessage("No payslips generated yet.")
                        .setPositiveButton("OK", null)
                        .show()
                    return@launch
                }

                val lines = list.map { "${monthNames[it.month]} ${it.year}  —  ₹${"%,.0f".format(it.netSalary)}" }.toTypedArray()
                AlertDialog.Builder(this@MainActivity)
                    .setTitle("Payslips")
                    .setItems(lines) { _, which ->
                        val p = list[which]
                        showPayslipDetail(p.month, p.year)
                    }
                    .setNegativeButton("Close", null)
                    .show()
            } catch (e: Exception) {
                Toast.makeText(this@MainActivity, "Could not load payslips: ${friendlyError(e)}", Toast.LENGTH_LONG).show()
            }
        }
    }

    private fun showPayslipDetail(month: Int, year: Int) {
        lifecycleScope.launch {
            try {
                val resp = com.seyalix.hr.api.ApiClient.apiService.getPayslip(month, year)
                if (!resp.isSuccessful || resp.body()?.success != true || resp.body()?.data == null) {
                    Toast.makeText(this@MainActivity, resp.body()?.message ?: "Payslip not available", Toast.LENGTH_SHORT).show()
                    return@launch
                }
                val d = resp.body()!!.data!!
                val p = d.payslip
                val e = d.employee
                val b = d.bank
                val a = d.attendance
                fun rupee(v: Float?) = "₹${"%,.0f".format(v ?: 0f)}"
                fun days(v: Float?) = if (v == null) null else if (v % 1f == 0f) "%.0f".format(v) else "%.1f".format(v)
                val msg = buildString {
                    d.company?.companyName?.let { append("$it\n") }
                    d.company?.address?.let { if (it.isNotBlank()) append("$it\n") }
                    append("\n")
                    append("${e.firstName} ${e.lastName}  (${e.employeeId})\n")
                    e.designation?.let { append("$it") }
                    e.department?.let { append(if (e.designation != null) " · $it\n" else "$it\n") }
                    e.uanNumber?.let { append("UAN: $it\n") }
                    e.panNumber?.let { append("PAN: $it\n") }
                    b?.bankName?.let { append("Bank: $it") }
                    b?.accountNumber?.let { append(if (b.bankName != null) " ($it)\n" else "A/C: $it\n") }
                    append("\n")
                    if (a != null) {
                        append("ATTENDANCE\n")
                        days(a.presentDays)?.let { append("Present: $it   ") }
                        days(a.absentDays)?.let { append("Absent: $it   ") }
                        days(a.lopDays)?.let { append("LOP: $it\n") } ?: append("\n")
                        days(a.leaveDays)?.let { append("Leave: $it   ") }
                        a.weekOffDays?.let { append("Week Off: $it   ") }
                        a.holidays?.let { append("Holidays: $it   ") }
                        days(a.odDays)?.let { append("On Duty: $it") }
                        append("\n\n")
                    }
                    append("EARNINGS\n")
                    append("Basic: ${rupee(p.basic)}\nHRA: ${rupee(p.hra)}\nDA: ${rupee(p.da)}\n")
                    append("Conveyance: ${rupee(p.conveyance)}\nMedical: ${rupee(p.medical)}\nSpecial Allowance: ${rupee(p.specialAllowance)}\n")
                    if ((p.lta ?: 0f) > 0f) append("LTA: ${rupee(p.lta)}\n")
                    if ((p.incentive ?: 0f) > 0f) append("Incentive: ${rupee(p.incentive)}\n")
                    if ((p.bonus ?: 0f) > 0f) append("Bonus: ${rupee(p.bonus)}\n")
                    if ((p.arrears ?: 0f) > 0f) append("Arrears: ${rupee(p.arrears)}\n")
                    append("Gross Salary: ${rupee(p.grossSalary)}\n\n")
                    append("DEDUCTIONS\n")
                    append("PF: ${rupee(p.pfEmployee)}\nESI: ${rupee(p.esiEmployee)}\n")
                    append("Professional Tax: ${rupee(p.professionalTax)}\nIncome Tax: ${rupee(p.incomeTax)}\n")
                    if ((p.advanceRecovery ?: 0f) > 0f) append("Advance Recovery: ${rupee(p.advanceRecovery)}\n")
                    if ((p.loanRecovery ?: 0f) > 0f) append("Loan Recovery: ${rupee(p.loanRecovery)}\n")
                    append("Total Deductions: ${rupee(p.totalDeductions)}\n\n")
                    append("NET PAY: ${rupee(p.netSalary)}\n\n")
                    append("This is a system-generated payslip and does not require a signature.")
                }

                AlertDialog.Builder(this@MainActivity)
                    .setTitle("${monthNames[month]} $year — Payslip")
                    .setMessage(msg)
                    .setPositiveButton("Share") { _, _ ->
                        val shareIntent = Intent(Intent.ACTION_SEND).apply {
                            type = "text/plain"
                            putExtra(Intent.EXTRA_SUBJECT, "Payslip - ${monthNames[month]} $year")
                            putExtra(Intent.EXTRA_TEXT, msg)
                        }
                        startActivity(Intent.createChooser(shareIntent, "Share Payslip"))
                    }
                    .setNeutralButton("Email Me") { _, _ -> emailMyPayslip(month, year) }
                    .setNegativeButton("Close", null)
                    .show()
            } catch (e: Exception) {
                Toast.makeText(this@MainActivity, "Could not load payslip: ${friendlyError(e)}", Toast.LENGTH_LONG).show()
            }
        }
    }

    // Self-serve "email me this payslip" — same professional HTML
    // payslip HR's bulk-send tool uses, but sent to the logged-in
    // employee's own registered email only.
    private fun emailMyPayslip(month: Int, year: Int) {
        Toast.makeText(this, "Sending…", Toast.LENGTH_SHORT).show()
        lifecycleScope.launch {
            try {
                val resp = com.seyalix.hr.api.ApiClient.apiService.emailMyPayslip(mapOf("month" to month, "year" to year))
                val body = resp.body()
                if (resp.isSuccessful && body?.success == true) {
                    Toast.makeText(this@MainActivity, body.message ?: "Payslip emailed", Toast.LENGTH_LONG).show()
                } else {
                    Toast.makeText(this@MainActivity, body?.message ?: "Could not send email", Toast.LENGTH_LONG).show()
                }
            } catch (e: Exception) {
                Toast.makeText(this@MainActivity, "Could not send email: ${friendlyError(e)}", Toast.LENGTH_LONG).show()
            }
        }
    }

    // Simple list dialog — pulls the same ?action=holidays endpoint the
    // Attendance calendar already uses, so it always matches what's
    // shown as holiday-colored days there.
    private fun showHolidayList() {
        lifecycleScope.launch {
            try {
                val year = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR)
                val resp = com.seyalix.hr.api.ApiClient.apiService.getHolidays(year)
                val holidays = if (resp.isSuccessful && resp.body()?.success == true)
                    resp.body()!!.data ?: emptyList() else emptyList()

                if (holidays.isEmpty()) {
                    AlertDialog.Builder(this@MainActivity)
                        .setTitle("Holiday List $year")
                        .setMessage("No holidays found for $year.")
                        .setPositiveButton("OK", null)
                        .show()
                    return@launch
                }

                val sdfIn = java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault())
                val sdfOut = java.text.SimpleDateFormat("EEE, dd MMM yyyy", java.util.Locale.getDefault())
                val lines = holidays.sortedBy { it.date }.map { h ->
                    val displayDate = try { sdfOut.format(sdfIn.parse(h.date)!!) } catch (e: Exception) { h.date }
                    "$displayDate  —  ${h.name}"
                }

                AlertDialog.Builder(this@MainActivity)
                    .setTitle("Holiday List $year")
                    .setItems(lines.toTypedArray(), null)
                    .setPositiveButton("Close", null)
                    .show()
            } catch (e: Exception) {
                Toast.makeText(this@MainActivity, "Could not load holidays: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun loadFragment(fragment: Fragment) {
        supportFragmentManager.beginTransaction()
            .replace(R.id.fragmentContainer, fragment)
            .commit()
    }

    // Called from DashboardFragment's logout button, or the "More" menu
    fun logout() {
        PrefsManager(this).logout()
        val intent = Intent(this, LoginActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        startActivity(intent)
        finish()
    }

    // FIX (15 Jul 2026): the "X Pending" badge on the dashboard had no
    // click handler at all — tapping it did nothing. DashboardFragment
    // calls this to jump straight to the Approvals screen.
    fun openApprovals() {
        prefs.setLastSeenApprovalCount(lastKnownTotal)
        updateApprovalBadge(0)
        loadFragment(ApprovalsFragment())
    }

    fun openNotifications() {
        prefs.setLastSeenNotifCount(lastKnownUnreadNotifs)
        loadFragment(NotificationsFragment())
    }

    companion object {
        private const val CHANNEL_ID = "pending_approvals"
        private const val APPROVAL_NOTIF_ID = 4201
        private const val NOTIF_CHANNEL_ID = "personal_notifications"
        private const val NOTIF_ID = 4202
    }
}
