package com.seyalix.hr.ui.attendance

import android.Manifest
import android.app.DatePickerDialog
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Bundle
import android.provider.Settings
import android.view.*
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
import com.google.android.gms.tasks.CancellationTokenSource
import com.seyalix.hr.R
import com.seyalix.hr.api.ApiClient
import com.seyalix.hr.models.AttendanceRecord
import com.seyalix.hr.models.Holiday
import com.seyalix.hr.models.LeaveApplication
import kotlinx.coroutines.launch
import kotlinx.coroutines.CancellationException
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale

class AttendanceFragment : Fragment() {

    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private val locationPermissionRequestCode = 1001

    private var pendingAction: String? = null
    private var pendingView: View? = null

    // Which month/year the calendar is currently showing — starts on the
    // current month, moves with the ‹ › arrows. Independent of "today"
    // (which stays fixed so future/past-day styling is always correct).
    private var displayedMonth: Int = 0
    private var displayedYear: Int = 0

    // day-of-month -> info, rebuilt every time data is (re)loaded
    private data class DayInfo(
        val type: String,        // "present" | "absent" | "leave" | "holiday" | "sunday" | "future" | "none"
        val label: String,       // shown in the detail dialog
        val checkIn: String? = null,
        val checkOut: String? = null
    )

    override fun onCreateView(inflater: LayoutInflater, c: ViewGroup?, s: Bundle?): View? =
        inflater.inflate(R.layout.fragment_attendance, c, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireActivity())

        val todayStr = SimpleDateFormat("EEEE, dd MMM yyyy", Locale.getDefault()).format(java.util.Date())
        view.findViewById<TextView>(R.id.tvToday).text = todayStr

        val cal = Calendar.getInstance()
        view.findViewById<TextView>(R.id.tvCalendarMonth).text =
            SimpleDateFormat("MMMM yyyy", Locale.getDefault()).format(cal.time).uppercase()

        view.findViewById<Button>(R.id.btnCheckIn).setOnClickListener {
            getLocationAndAction("check_in", view)
        }

        view.findViewById<Button>(R.id.btnCheckOut).setOnClickListener {
            getLocationAndAction("check_out", view)
        }

        view.findViewById<LinearLayout>(R.id.rowApplyPermission).setOnClickListener {
            showPermissionDialog()
        }

        view.findViewById<LinearLayout>(R.id.rowApplyLate).setOnClickListener {
            showLateDialog()
        }

        view.findViewById<TextView>(R.id.btnAttendanceHistory).setOnClickListener {
            // "Today" — jump the calendar back to the current month
            displayedMonth = cal.get(Calendar.MONTH) + 1
            displayedYear = cal.get(Calendar.YEAR)
            loadEverything(view)
        }

        view.findViewById<TextView>(R.id.btnPrevMonth).setOnClickListener {
            displayedMonth -= 1
            if (displayedMonth < 1) { displayedMonth = 12; displayedYear -= 1 }
            loadEverything(view)
        }

        view.findViewById<TextView>(R.id.btnNextMonth).setOnClickListener {
            displayedMonth += 1
            if (displayedMonth > 12) { displayedMonth = 1; displayedYear += 1 }
            loadEverything(view)
        }

        displayedMonth = cal.get(Calendar.MONTH) + 1
        displayedYear = cal.get(Calendar.YEAR)

        buildWeekdayHeader(view)
        loadEverything(view)
    }

    // ── GPS: Get location then call API ───────────────────────
    private fun getLocationAndAction(action: String, view: View) {
        val locationManager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
        if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            AlertDialog.Builder(requireContext())
                .setTitle("GPS Required")
                .setMessage("GPS must be turned ON for attendance marking. Please enable location.")
                .setPositiveButton("Turn On GPS") { _, _ ->
                    startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
                }
                .setNegativeButton("Cancel", null)
                .show()
            return
        }

        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION),
                locationPermissionRequestCode
            )
            pendingAction = action
            pendingView = view
            return
        }

        val btn = if (action == "check_in")
            view.findViewById<Button>(R.id.btnCheckIn)
        else
            view.findViewById<Button>(R.id.btnCheckOut)

        val originalText = btn.text
        btn.text = "Getting GPS…"
        btn.isEnabled = false

        val cts = CancellationTokenSource()
        fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, cts.token)
            .addOnSuccessListener { location ->
                btn.text = originalText
                btn.isEnabled = true

                if (location == null) {
                    Toast.makeText(context,
                        "Could not get GPS location. Please try again.",
                        Toast.LENGTH_LONG).show()
                    return@addOnSuccessListener
                }

                lifecycleScope.launch {
                    try {
                        val body = mapOf(
                            "latitude" to location.latitude.toString(),
                            "longitude" to location.longitude.toString()
                        )
                        val resp = if (action == "check_in")
                            ApiClient.apiService.checkIn(body)
                        else
                            ApiClient.apiService.checkOut(body)

                        val msg = resp.body()?.message ?: "Done"

                        if (resp.isSuccessful && resp.body()?.success == true) {
                            Toast.makeText(context, "✅ $msg", Toast.LENGTH_LONG).show()
                            loadEverything(view)
                        } else {
                            AlertDialog.Builder(requireContext())
                                .setTitle("⚠️ Cannot Mark Attendance")
                                .setMessage(msg)
                                .setPositiveButton("OK", null)
                                .show()
                        }
                    } catch (e: CancellationException) {
                        throw e  // normal when the screen changes mid-request — not a real error
                    } catch (e: Exception) {
                        Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
                    }
                }
            }
            .addOnFailureListener {
                btn.text = originalText
                btn.isEnabled = true
                Toast.makeText(context, "GPS error: ${it.message}", Toast.LENGTH_SHORT).show()
            }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == locationPermissionRequestCode) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                pendingAction?.let { action ->
                    pendingView?.let { view ->
                        getLocationAndAction(action, view)
                    }
                }
            } else {
                Toast.makeText(context,
                    "Location permission required for attendance marking.",
                    Toast.LENGTH_LONG).show()
            }
            pendingAction = null
            pendingView = null
        }
    }

    // ── Permission Dialog (no GPS needed) ─────────────────────
    private fun showPermissionDialog() {
        val ctx = requireContext()
        val cal = Calendar.getInstance()
        DatePickerDialog(ctx, { _, y, m, d ->
            val date = "%04d-%02d-%02d".format(y, m + 1, d)
            pickTime(ctx) { fh, fm ->
                val from = "%02d:%02d".format(fh, fm)
                pickTime(ctx) { th, tm ->
                    val to = "%02d:%02d".format(th, tm)
                    val etReason = EditText(ctx).apply { hint = "Reason (optional)" }
                    AlertDialog.Builder(ctx)
                        .setTitle("Apply Permission – $date")
                        .setView(etReason)
                        .setPositiveButton("Submit") { _, _ ->
                            val reason = etReason.text.toString().trim().ifBlank { "Permission" }
                            submitPermission(date, from, to, reason)
                        }
                        .setNegativeButton("Cancel", null)
                        .show()
                }
            }
        }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
    }

    private fun pickTime(ctx: Context, onPicked: (Int, Int) -> Unit) {
        val cal = Calendar.getInstance()
        android.app.TimePickerDialog(ctx, { _, h, m -> onPicked(h, m) },
            cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show()
    }

    private fun submitPermission(date: String, from: String, to: String, reason: String) {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.applyPermission(
                    mapOf("date" to date, "from_time" to from, "to_time" to to, "reason" to reason)
                )
                if (resp.isSuccessful && resp.body()?.success == true) {
                    Toast.makeText(context, "✅ ${resp.body()?.message ?: "Permission submitted"}", Toast.LENGTH_LONG).show()
                } else {
                    Toast.makeText(context, resp.body()?.message ?: "Could not submit request", Toast.LENGTH_LONG).show()
                }
            } catch (e: CancellationException) {
                throw e  // normal when the screen changes mid-request — not a real error
            } catch (e: Exception) {
                Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // ── Late Dialog (no GPS needed) ───────────────────────────
    private fun showLateDialog() {
        val ctx = requireContext()
        val cal = Calendar.getInstance()
        DatePickerDialog(ctx, { _, y, m, d ->
            val date = "%04d-%02d-%02d".format(y, m + 1, d)
            val etReason = EditText(ctx).apply { hint = "Reason (optional)" }
            AlertDialog.Builder(ctx)
                .setTitle("Late Regularization – $date")
                .setView(etReason)
                .setPositiveButton("Submit") { _, _ ->
                    val reason = etReason.text.toString().trim().ifBlank { "Late arrival" }
                    submitLateRegularization(date, reason)
                }
                .setNegativeButton("Cancel", null)
                .show()
        }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
    }

    private fun submitLateRegularization(date: String, reason: String) {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.applyLateRegularization(
                    mapOf("date" to date, "reason" to reason)
                )
                if (resp.isSuccessful && resp.body()?.success == true) {
                    Toast.makeText(context, "✅ ${resp.body()?.message ?: "Submitted"}", Toast.LENGTH_LONG).show()
                } else {
                    Toast.makeText(context, resp.body()?.message ?: "Could not submit request", Toast.LENGTH_LONG).show()
                }
            } catch (e: CancellationException) {
                throw e  // normal when the screen changes mid-request — not a real error
            } catch (e: Exception) {
                Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // ── Weekday header (Sun–Sat) ───────────────────────────────
    private fun buildWeekdayHeader(view: View) {
        val grid = view.findViewById<GridLayout>(R.id.gridWeekdayHeader)
        grid.removeAllViews()
        val labels = listOf("S", "M", "T", "W", "T", "F", "S")
        val cellSizeDp = 40
        val density = resources.displayMetrics.density
        labels.forEach { label ->
            val tv = TextView(requireContext())
            tv.text = label
            tv.gravity = Gravity.CENTER
            tv.textSize = 11f
            tv.setTextColor(0xFF9E9E9E.toInt())
            val params = GridLayout.LayoutParams()
            params.width = (cellSizeDp * density).toInt()
            params.height = (24 * density).toInt()
            tv.layoutParams = params
            grid.addView(tv)
        }
    }

    // ── Load attendance + leaves + holidays, then render calendar ─
    private fun loadEverything(view: View) {
        val month = displayedMonth
        val year = displayedYear
        val todayDateStr = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(java.util.Date())

        val monthCal = Calendar.getInstance().apply { set(year, month - 1, 1) }
        view.findViewById<TextView>(R.id.tvCalendarMonth).text =
            SimpleDateFormat("MMMM yyyy", Locale.getDefault()).format(monthCal.time).uppercase()

        lifecycleScope.launch {
            var records: List<AttendanceRecord> = emptyList()
            var leaves: List<LeaveApplication> = emptyList()
            var holidays: List<Holiday> = emptyList()

            try {
                val resp = ApiClient.apiService.getAttendanceHistory(month, year)
                if (resp.isSuccessful && resp.body()?.success == true) records = resp.body()!!.data ?: emptyList()
            } catch (_: Exception) { }

            try {
                val resp = ApiClient.apiService.getLeaveApplications()
                if (resp.isSuccessful && resp.body()?.success == true) leaves = resp.body()!!.data ?: emptyList()
            } catch (_: Exception) { }

            try {
                // Holidays for both the displayed year AND the current real
                // year — needed when navigating across a Dec→Jan boundary,
                // and cheap to just merge both if they're the same year.
                val realYear = Calendar.getInstance().get(Calendar.YEAR)
                val resp = ApiClient.apiService.getHolidays(year)
                if (resp.isSuccessful && resp.body()?.success == true) holidays = resp.body()!!.data ?: emptyList()
                if (year != realYear) {
                    val resp2 = ApiClient.apiService.getHolidays(realYear)
                    if (resp2.isSuccessful && resp2.body()?.success == true) {
                        holidays = holidays + (resp2.body()!!.data ?: emptyList())
                    }
                }
            } catch (_: Exception) { }

            activity?.runOnUiThread {
                renderTodayStatus(view, records.find { it.date == todayDateStr })
                renderCalendar(view, month, year, records, leaves, holidays)
            }
        }
    }

    private fun renderTodayStatus(view: View, todayRecord: AttendanceRecord?) {
        val tvIn = view.findViewById<TextView>(R.id.tvCheckInTime)
        val tvOut = view.findViewById<TextView>(R.id.tvCheckOutTime)
        val tvPill = view.findViewById<TextView>(R.id.tvStatusPill)

        tvIn.text = todayRecord?.checkIn ?: "— —"
        tvOut.text = todayRecord?.checkOut ?: "— —"

        when {
            todayRecord?.status == "OD" -> {
                tvPill.text = "Present (On Duty)"
                tvPill.setBackgroundResource(R.drawable.badge_green)
            }
            todayRecord?.checkIn != null && todayRecord.checkOut != null -> {
                tvPill.text = "Completed"
                tvPill.setBackgroundResource(R.drawable.badge_blue)
            }
            todayRecord?.checkIn != null -> {
                tvPill.text = "Checked In"
                tvPill.setBackgroundResource(R.drawable.badge_green)
            }
            else -> {
                tvPill.text = "Not marked"
                tvPill.setBackgroundResource(R.drawable.badge_gray)
            }
        }
    }

    // ── Build the calendar grid for the given month ─────────────
    private fun renderCalendar(
        view: View,
        month: Int,
        year: Int,
        records: List<AttendanceRecord>,
        leaves: List<LeaveApplication>,
        holidays: List<Holiday>
    ) {
        val recordsByDate = records.associateBy { it.date }
        val holidaysByDate = holidays.associateBy { it.date }

        val cal = Calendar.getInstance()
        cal.set(year, month - 1, 1)
        val daysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH)
        val firstDayOfWeek = cal.get(Calendar.DAY_OF_WEEK) - 1 // 0=Sunday

        val todayCal = Calendar.getInstance()
        val isCurrentMonth = todayCal.get(Calendar.MONTH) + 1 == month && todayCal.get(Calendar.YEAR) == year
        val todayDay = todayCal.get(Calendar.DAY_OF_MONTH)

        val dayInfos = mutableMapOf<Int, DayInfo>()

        for (day in 1..daysInMonth) {
            val dateStr = "%04d-%02d-%02d".format(year, month, day)
            cal.set(year, month - 1, day)
            val dow = cal.get(Calendar.DAY_OF_WEEK)
            val isSunday = dow == Calendar.SUNDAY
            val isFuture = isCurrentMonth && day > todayDay || (!isCurrentMonth && cal.after(todayCal))

            val holiday = holidaysByDate[dateStr]
            val leave = leaves.find { l ->
                l.status != "rejected" && l.status != "cancelled" &&
                    dateStr >= l.startDate && dateStr <= l.endDate
            }
            val record = recordsByDate[dateStr]

            dayInfos[day] = when {
                holiday != null -> DayInfo("holiday", holiday.name)
                leave != null -> DayInfo(
                    if (leave.status == "pending") "leave_pending" else "leave",
                    "${leave.leaveTypeName ?: "Leave"} (${leave.status})"
                )
                // On Duty counts as Present — the employee is away on official
                // work, not physically punching in/out, so checkIn is usually
                // null here. Without this check it fell through to "absent".
                record?.status == "OD" -> DayInfo("onduty", "Present (On Duty)", record.checkIn, record.checkOut)
                record?.checkIn != null -> DayInfo("present", "Present", record.checkIn, record.checkOut)
                isSunday -> DayInfo("sunday", "Sunday")
                isFuture -> DayInfo("future", "Upcoming")
                record != null -> DayInfo("absent", "Absent")
                else -> DayInfo("none", "No record")
            }
        }

        val grid = view.findViewById<GridLayout>(R.id.gridCalendar)
        grid.removeAllViews()
        val density = resources.displayMetrics.density
        val cellSizePx = (40 * density).toInt()

        // Leading blank cells to align the 1st with its weekday
        repeat(firstDayOfWeek) {
            val blank = View(requireContext())
            val params = GridLayout.LayoutParams()
            params.width = cellSizePx
            params.height = cellSizePx
            blank.layoutParams = params
            grid.addView(blank)
        }

        for (day in 1..daysInMonth) {
            val info = dayInfos[day]!!
            val cellView = layoutInflater.inflate(R.layout.item_calendar_day, grid, false)
            val tvDay = cellView.findViewById<TextView>(R.id.tvDayNumber)
            tvDay.text = day.toString()

            val bgRes = when (info.type) {
                "present" -> R.drawable.badge_green
                "onduty" -> R.drawable.badge_green
                "absent" -> R.drawable.badge_red
                "leave" -> R.drawable.badge_purple
                "leave_pending" -> R.drawable.badge_orange
                "holiday" -> R.drawable.badge_blue
                "sunday" -> R.drawable.badge_gray
                else -> null
            }
            if (bgRes != null) {
                tvDay.setBackgroundResource(bgRes)
                tvDay.setTextColor(0xFFFFFFFF.toInt())
            } else {
                tvDay.setBackgroundResource(R.drawable.input_bg)
                tvDay.setTextColor(0xFF9E9E9E.toInt())
            }

            cellView.setOnClickListener {
                val dateStr = "%04d-%02d-%02d".format(year, month, day)
                val msg = when (info.type) {
                    "present" -> "In: ${info.checkIn ?: "—"}\nOut: ${info.checkOut ?: "—"}"
                    else -> info.label
                }
                AlertDialog.Builder(requireContext())
                    .setTitle(dateStr)
                    .setMessage(msg)
                    .setPositiveButton("OK", null)
                    .show()
            }
            grid.addView(cellView)
        }
    }
}
