package com.seyalix.hr.ui.leave

import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.os.Bundle
import android.view.*
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.seyalix.hr.R
import com.seyalix.hr.api.ApiClient
import com.seyalix.hr.models.LeaveApplication
import com.seyalix.hr.models.LeaveType
import com.seyalix.hr.models.OnDutyRequest
import kotlinx.coroutines.launch
import kotlinx.coroutines.CancellationException
import java.util.*

class LeaveFragment : Fragment() {

    private var leaveTypes: List<LeaveType> = emptyList()

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

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

        loadLeaveBalance(view)
        loadLeaveTypes(view)
        loadMyLeaves(view)
        loadMyOnDuty(view)

        view.findViewById<Button>(R.id.btnApplyLeave).setOnClickListener {
            showApplyLeaveDialog(view)
        }
        view.findViewById<Button>(R.id.btnApplyOnDuty).setOnClickListener {
            showApplyOnDutyDialog(view)
        }
    }

    private fun loadLeaveBalance(view: View) {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.getLeaveBalance()
                if (resp.isSuccessful && resp.body()?.success == true) {
                    val balances = resp.body()!!.data ?: emptyList()
                    activity?.runOnUiThread {
                        val container = view.findViewById<LinearLayout>(R.id.llBalanceRows)
                        val tvEmpty = view.findViewById<TextView>(R.id.tvBalanceEmpty)
                        container.removeAllViews()

                        if (balances.isEmpty()) {
                            tvEmpty.text = "No leave balance data"
                            tvEmpty.visibility = View.VISIBLE
                        } else {
                            tvEmpty.visibility = View.GONE
                            balances.forEach { b ->
                                val row = layoutInflater.inflate(R.layout.item_leave_balance_row, container, false)
                                val monthlyTag = when {
                                    b.accrualPeriod != "monthly" -> ""
                                    b.policyCarryForward == 1 -> "  •  THIS MONTH (unused carries forward)"
                                    b.policyEncashable == 1 -> "  •  THIS MONTH (unused is encashed)"
                                    else -> "  •  THIS MONTH (unused lapses)"
                                }
                                row.findViewById<TextView>(R.id.tvBalanceName).text = "${b.name} (${b.code})$monthlyTag"
                                row.findViewById<TextView>(R.id.tvBalanceCount).text =
                                    "${b.available.toInt()} / ${b.allocated.toInt()}"
                                container.addView(row)
                            }
                        }
                    }
                }
            } catch (e: CancellationException) {
                throw e  // normal when the screen changes mid-request — not a real error
            } catch (e: Exception) {
                activity?.runOnUiThread {
                    view.findViewById<TextView>(R.id.tvBalanceEmpty).apply {
                        text = "Could not load leave balance"
                        visibility = View.VISIBLE
                    }
                }
            }
        }
    }

    private fun loadLeaveTypes(view: View) {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.getLeaveTypes()
                if (resp.isSuccessful && resp.body()?.success == true) {
                    leaveTypes = resp.body()!!.data ?: emptyList()
                } else {
                    activity?.runOnUiThread {
                        Toast.makeText(context, resp.body()?.message ?: "Could not load leave types", Toast.LENGTH_SHORT).show()
                    }
                }
            } catch (e: CancellationException) {
                throw e  // normal when the screen changes mid-request — not a real error
            } catch (e: Exception) {
                activity?.runOnUiThread {
                    Toast.makeText(context, "Error loading leave types: ${e.message}", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    private fun showApplyLeaveDialog(rootView: View) {
        val ctx = requireContext()
        if (leaveTypes.isEmpty()) {
            Toast.makeText(ctx, "Still loading leave types — please try again in a moment", Toast.LENGTH_SHORT).show()
            loadLeaveTypes(rootView) // retry
            return
        }

        val typeNames = leaveTypes.map { it.name }.toTypedArray()
        var selectedTypeIdx = 0
        var startDate = ""
        var endDate   = ""

        val cal = Calendar.getInstance()
        val dialogView = layoutInflater.inflate(R.layout.dialog_apply_leave, null)
        val spinnerType  = dialogView.findViewById<Spinner>(R.id.spinnerLeaveType)
        val tvStart      = dialogView.findViewById<TextView>(R.id.tvStartDate)
        val tvEnd        = dialogView.findViewById<TextView>(R.id.tvEndDate)
        val etReason     = dialogView.findViewById<EditText>(R.id.etLeaveReason)

        spinnerType.adapter = ArrayAdapter(ctx, android.R.layout.simple_spinner_dropdown_item, typeNames)
        spinnerType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(p: AdapterView<*>?, v: View?, pos: Int, id: Long) { selectedTypeIdx = pos }
            override fun onNothingSelected(p: AdapterView<*>?) {}
        }

        tvStart.setOnClickListener {
            DatePickerDialog(ctx, { _, y, m, d ->
                startDate = "%04d-%02d-%02d".format(y, m + 1, d)
                tvStart.text = startDate
                tvStart.setTextColor(0xFF212121.toInt())
            }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
        }
        tvEnd.setOnClickListener {
            DatePickerDialog(ctx, { _, y, m, d ->
                endDate = "%04d-%02d-%02d".format(y, m + 1, d)
                tvEnd.text = endDate
                tvEnd.setTextColor(0xFF212121.toInt())
            }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
        }

        AlertDialog.Builder(ctx)
            .setTitle("Apply Leave")
            .setView(dialogView)
            .setPositiveButton("Submit") { _, _ ->
                if (startDate.isEmpty() || endDate.isEmpty()) {
                    Toast.makeText(ctx, "Please select both dates", Toast.LENGTH_SHORT).show(); return@setPositiveButton
                }
                val reason = etReason.text.toString()
                if (reason.isBlank()) { Toast.makeText(ctx, "Please enter a reason", Toast.LENGTH_SHORT).show(); return@setPositiveButton }

                val typeId = leaveTypes[selectedTypeIdx].id
                lifecycleScope.launch {
                    try {
                        val resp = ApiClient.apiService.applyLeave(
                            mapOf(
                                "leave_type_id" to typeId.toString(),
                                "start_date"    to startDate,
                                "end_date"      to endDate,
                                "reason"        to reason
                            )
                        )
                        if (resp.isSuccessful && resp.body()?.success == true) {
                            Toast.makeText(context, "✅ ${resp.body()?.message ?: "Leave applied"}", Toast.LENGTH_LONG).show()
                        } else {
                            // Surfaces backend messages like "Insufficient leave balance. Available: 0"
                            AlertDialog.Builder(ctx)
                                .setTitle("⚠️ Cannot Apply Leave")
                                .setMessage(resp.body()?.message ?: "Something went wrong")
                                .setPositiveButton("OK", null)
                                .show()
                        }
                        loadLeaveBalance(rootView)
                        loadMyLeaves(rootView)
                    } 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()
                    }
                }
            }
            .setNegativeButton("Cancel", null)
            .show()
    }

    private fun loadMyLeaves(view: View) {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.getLeaveApplications()
                if (resp.isSuccessful && resp.body()?.success == true) {
                    val leaves = resp.body()!!.data ?: emptyList()
                    activity?.runOnUiThread { renderLeaves(view, leaves) }
                }
            } catch (e: CancellationException) {
                throw e  // normal when the screen changes mid-request — not a real error
            } catch (e: Exception) { /* history is non-critical; fail quietly */ }
        }
    }

    private fun renderLeaves(view: View, leaves: List<LeaveApplication>) {
        val lv = view.findViewById<ListView>(R.id.lvMyLeaves)
        lv.adapter = object : ArrayAdapter<LeaveApplication>(requireContext(), 0, leaves) {
            override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
                val row = convertView ?: layoutInflater.inflate(R.layout.item_leave_row, parent, false)
                val l = leaves[position]

                row.findViewById<TextView>(R.id.tvRowType).text = l.leaveTypeName ?: "Leave"
                row.findViewById<TextView>(R.id.tvRowDates).text =
                    "${l.startDate} → ${l.endDate}  (${l.days.toInt()}d)"
                row.findViewById<TextView>(R.id.tvRowReason).text = l.reason

                val tvStatus = row.findViewById<TextView>(R.id.tvRowStatus)
                tvStatus.text = l.status.replaceFirstChar { it.uppercase() }
                tvStatus.setBackgroundResource(
                    when (l.status.lowercase()) {
                        "approved" -> R.drawable.badge_green
                        "rejected" -> R.drawable.badge_red
                        "pending" -> R.drawable.badge_orange
                        "cancelled" -> R.drawable.badge_gray
                        else -> R.drawable.badge_gray
                    }
                )
                return row
            }
        }
    }

    // ── On Duty ──────────────────────────────────────────────
    private fun showApplyOnDutyDialog(rootView: View) {
        val ctx = requireContext()
        var startDate = ""
        var endDate = ""
        var startTime = ""
        var endTime = ""
        val cal = Calendar.getInstance()

        val dialogView = layoutInflater.inflate(R.layout.dialog_apply_onduty, null)
        val etPurpose   = dialogView.findViewById<EditText>(R.id.etOnDutyPurpose)
        val etLocation  = dialogView.findViewById<EditText>(R.id.etOnDutyLocation)
        val tvStart     = dialogView.findViewById<TextView>(R.id.tvOnDutyStartDate)
        val tvEnd       = dialogView.findViewById<TextView>(R.id.tvOnDutyEndDate)
        val etReason    = dialogView.findViewById<EditText>(R.id.etOnDutyReason)
        val rgType      = dialogView.findViewById<RadioGroup>(R.id.rgOnDutyType)
        val rbFull      = dialogView.findViewById<RadioButton>(R.id.rbOnDutyFull)
        val llEndWrap   = dialogView.findViewById<View>(R.id.llOnDutyEndDateWrap)
        val llTimeRow   = dialogView.findViewById<View>(R.id.llOnDutyTimeRow)
        val tvPartHint  = dialogView.findViewById<View>(R.id.tvOnDutyPartHint)
        val tvStartTime = dialogView.findViewById<TextView>(R.id.tvOnDutyStartTime)
        val tvEndTime   = dialogView.findViewById<TextView>(R.id.tvOnDutyEndTime)

        rgType.setOnCheckedChangeListener { _, _ ->
            val isPartial = !rbFull.isChecked
            llEndWrap.visibility = if (isPartial) View.GONE else View.VISIBLE
            llTimeRow.visibility = if (isPartial) View.VISIBLE else View.GONE
            tvPartHint.visibility = if (isPartial) View.VISIBLE else View.GONE
            if (isPartial && startDate.isNotEmpty()) {
                endDate = startDate
                tvEnd.text = endDate
            }
        }

        tvStart.setOnClickListener {
            DatePickerDialog(ctx, { _, y, m, d ->
                startDate = "%04d-%02d-%02d".format(y, m + 1, d)
                tvStart.text = startDate
                tvStart.setTextColor(0xFF212121.toInt())
                if (!rbFull.isChecked) { endDate = startDate; tvEnd.text = endDate }
            }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
        }
        tvEnd.setOnClickListener {
            DatePickerDialog(ctx, { _, y, m, d ->
                endDate = "%04d-%02d-%02d".format(y, m + 1, d)
                tvEnd.text = endDate
                tvEnd.setTextColor(0xFF212121.toInt())
            }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
        }
        tvStartTime.setOnClickListener {
            TimePickerDialog(ctx, { _, h, min ->
                startTime = "%02d:%02d:00".format(h, min)
                tvStartTime.text = String.format("%02d:%02d", h, min)
                tvStartTime.setTextColor(0xFF212121.toInt())
            }, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show()
        }
        tvEndTime.setOnClickListener {
            TimePickerDialog(ctx, { _, h, min ->
                endTime = "%02d:%02d:00".format(h, min)
                tvEndTime.text = String.format("%02d:%02d", h, min)
                tvEndTime.setTextColor(0xFF212121.toInt())
            }, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show()
        }

        AlertDialog.Builder(ctx)
            .setTitle("Apply On Duty")
            .setView(dialogView)
            .setPositiveButton("Submit") { _, _ ->
                val purpose = etPurpose.text.toString()
                val isFullDay = rbFull.isChecked
                if (purpose.isBlank()) {
                    Toast.makeText(ctx, "Please enter a purpose", Toast.LENGTH_SHORT).show(); return@setPositiveButton
                }
                if (startDate.isEmpty() || endDate.isEmpty()) {
                    Toast.makeText(ctx, "Please select the date(s)", Toast.LENGTH_SHORT).show(); return@setPositiveButton
                }
                if (!isFullDay && (startTime.isEmpty() || endTime.isEmpty())) {
                    Toast.makeText(ctx, "Please select start and end time", Toast.LENGTH_SHORT).show(); return@setPositiveButton
                }

                lifecycleScope.launch {
                    try {
                        val body = mutableMapOf(
                            "start_date"  to startDate,
                            "end_date"    to endDate,
                            "purpose"     to purpose,
                            "location"    to etLocation.text.toString(),
                            "reason"      to etReason.text.toString(),
                            "is_full_day" to if (isFullDay) "1" else "0"
                        )
                        if (!isFullDay) {
                            body["from_time"] = startTime
                            body["to_time"] = endTime
                        }
                        val resp = ApiClient.apiService.applyOnDuty(body)
                        if (resp.isSuccessful && resp.body()?.success == true) {
                            Toast.makeText(context, "✅ ${resp.body()?.message ?: "On Duty request submitted"}", Toast.LENGTH_LONG).show()
                        } else {
                            AlertDialog.Builder(ctx)
                                .setTitle("⚠️ Cannot Apply On Duty")
                                .setMessage(resp.body()?.message ?: "Something went wrong")
                                .setPositiveButton("OK", null)
                                .show()
                        }
                        loadMyOnDuty(rootView)
                    } 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()
                    }
                }
            }
            .setNegativeButton("Cancel", null)
            .show()
    }

    private fun loadMyOnDuty(view: View) {
        lifecycleScope.launch {
            try {
                val resp = ApiClient.apiService.getMyOnDuty()
                if (resp.isSuccessful && resp.body()?.success == true) {
                    val reqs = resp.body()!!.data ?: emptyList()
                    activity?.runOnUiThread { renderOnDuty(view, reqs) }
                }
            } catch (e: CancellationException) {
                throw e  // normal when the screen changes mid-request — not a real error
            } catch (e: Exception) { /* history is non-critical; fail quietly */ }
        }
    }

    private fun renderOnDuty(view: View, reqs: List<OnDutyRequest>) {
        val lv = view.findViewById<ListView>(R.id.lvMyOnDuty)
        lv.adapter = object : ArrayAdapter<OnDutyRequest>(requireContext(), 0, reqs) {
            override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
                val row = convertView ?: layoutInflater.inflate(R.layout.item_leave_row, parent, false)
                val r = reqs[position]

                row.findViewById<TextView>(R.id.tvRowType).text = r.purpose
                row.findViewById<TextView>(R.id.tvRowDates).text =
                    if (r.isFullDay == 1) {
                        "${r.startDate} → ${r.endDate}  (${r.days.toInt()}d, Full Day)" +
                            (if (!r.location.isNullOrBlank()) " · ${r.location}" else "")
                    } else {
                        "${r.startDate}  ${r.fromTime?.take(5) ?: ""}–${r.toTime?.take(5) ?: ""}  (Part Day)" +
                            (if (!r.location.isNullOrBlank()) " · ${r.location}" else "")
                    }
                row.findViewById<TextView>(R.id.tvRowReason).text = r.reason ?: ""

                val tvStatus = row.findViewById<TextView>(R.id.tvRowStatus)
                tvStatus.text = r.status.replaceFirstChar { it.uppercase() }
                tvStatus.setBackgroundResource(
                    when (r.status.lowercase()) {
                        "approved" -> R.drawable.badge_green
                        "rejected" -> R.drawable.badge_red
                        "pending" -> R.drawable.badge_orange
                        "cancelled" -> R.drawable.badge_gray
                        else -> R.drawable.badge_gray
                    }
                )
                return row
            }
        }
    }
}
