<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Shift_model extends CI_Model
{
    const TABLE        = 'tb_shift';
    const TABLE_ASSIGN = 'tb_assignment_shift';
    const ALL_DAYS     = 0;

    // ----------------------------------------------------------------
    // MASTER SHIFT
    // ----------------------------------------------------------------

    public function get_all($active_only = TRUE)
    {
        if ($active_only) {
            $this->db->where('is_active', 1);
        }
        return $this->db->order_by('jam_masuk', 'ASC')
                        ->get(self::TABLE)->result();
    }

    public function get_by_id($id)
    {
        return $this->db->where('id_shift', (int) $id)
                        ->get(self::TABLE)->row();
    }

    public function get_default()
    {
        return $this->db->where('is_default', 1)
                        ->where('is_active', 1)
                        ->limit(1)
                        ->get(self::TABLE)->row();
    }

    public function insert($data)
    {
        return $this->db->insert(self::TABLE, $data);
    }

    public function update($id, $data)
    {
        return $this->db->where('id_shift', (int) $id)
                        ->update(self::TABLE, $data);
    }

    public function set_default($id)
    {
        // Hapus flag default dari semua shift dulu
        $this->db->update(self::TABLE, ['is_default' => 0]);
        // Set default ke shift yang dipilih
        $this->db->where('id_shift', (int) $id)
                 ->update(self::TABLE, ['is_default' => 1]);
        // Update konfigurasi default_shift_id
        $this->db->where('kunci', 'default_shift_id')
                 ->update('tb_konfigurasi', ['nilai' => (string) $id]);
    }

    // ----------------------------------------------------------------
    // ASSIGNMENT SHIFT
    // ----------------------------------------------------------------

    public function get_assignments($tipe = NULL, $id_referensi = NULL)
    {
        $this->db->select('a.*, s.nama_shift, s.jam_masuk, s.jam_keluar, s.toleransi_menit')
                 ->from(self::TABLE_ASSIGN . ' a')
                 ->join(self::TABLE . ' s', 's.id_shift = a.id_shift', 'left');

        if ($tipe) {
            $this->db->where('a.tipe', $tipe);
        }
        if ($id_referensi) {
            $this->db->where('a.id_referensi', (int) $id_referensi);
        }
        return $this->db->order_by('a.berlaku_mulai', 'DESC')
                        ->get()->result();
    }

    public function get_assignment_by_id($id)
    {
        return $this->db->where('id_assignment', (int) $id)
                        ->get(self::TABLE_ASSIGN)->row();
    }

    public function insert_assignment($data)
    {
        return $this->db->insert(self::TABLE_ASSIGN, $data);
    }

    public function upsert_assignment_day($data)
    {
        $this->db->from(self::TABLE_ASSIGN)
                 ->where('tipe', $data['tipe'])
                 ->where('id_referensi', (int) $data['id_referensi'])
                 ->where('hari_ke', (int) $data['hari_ke'])
                 ->where('berlaku_mulai', $data['berlaku_mulai']);

        if (!empty($data['berlaku_selesai'])) {
            $this->db->where('berlaku_selesai', $data['berlaku_selesai']);
        } else {
            $this->db->where('berlaku_selesai IS NULL', NULL, FALSE);
        }

        $existing = $this->db->get()->row();
        if ($existing) {
            return $this->update_assignment($existing->id_assignment, $data);
        }

        return $this->insert_assignment($data);
    }

    public function update_assignment($id, $data)
    {
        return $this->db->where('id_assignment', (int) $id)
                        ->update(self::TABLE_ASSIGN, $data);
    }

    public function delete_assignment($id)
    {
        return $this->db->where('id_assignment', (int) $id)
                        ->delete(self::TABLE_ASSIGN);
    }

    // ----------------------------------------------------------------
    // LOOKUP JADWAL EFEKTIF UNTUK PEGAWAI PADA TANGGAL TERTENTU
    // Prioritas: assignment per PEGAWAI > assignment per DIVISI > fallback default
    // ----------------------------------------------------------------

    /**
    * Ambil jadwal efektif yang berlaku untuk satu pegawai pada tanggal tertentu.
    * Return: object jadwal efektif, atau NULL jika tidak ada fallback aktif.
     *
     * @param int    $id_pegawai
     * @param int    $id_divisi   divisi pegawai (untuk fallback level divisi)
     * @param string $tanggal     format Y-m-d
     * @return object|NULL
     */
    public function get_schedule_for_pegawai($id_pegawai, $id_divisi, $tanggal)
    {
        $hari_ke = (int) date('N', strtotime($tanggal));

        // 1. Cek assignment level PEGAWAI per hari
        $result = $this->_query_assignment('PEGAWAI', $id_pegawai, $tanggal, $hari_ke, TRUE);
        if ($result) {
            return $result;
        }

        // 2. Cek assignment level PEGAWAI generic
        $result = $this->_query_assignment('PEGAWAI', $id_pegawai, $tanggal, self::ALL_DAYS, FALSE);
        if ($result) {
            return $result;
        }

        // 3. Cek assignment level DIVISI per hari
        $result = $this->_query_assignment('DIVISI', $id_divisi, $tanggal, $hari_ke, TRUE);
        if ($result) {
            return $result;
        }

        // 4. Cek assignment level DIVISI generic
        $result = $this->_query_assignment('DIVISI', $id_divisi, $tanggal, self::ALL_DAYS, FALSE);
        if ($result) {
            return $result;
        }

        // 5. Fallback default: Senin-Jumat pakai shift default, Sabtu-Minggu off
        return $this->_build_default_schedule($tanggal);
    }

    public function get_shift_for_pegawai($id_pegawai, $id_divisi, $tanggal)
    {
        $schedule = $this->get_schedule_for_pegawai($id_pegawai, $id_divisi, $tanggal);
        if (!$schedule || (int) $schedule->is_hari_kerja !== 1 || empty($schedule->id_shift)) {
            return NULL;
        }

        return $schedule;
    }

    private function _query_assignment($tipe, $id_referensi, $tanggal, $hari_ke, $is_spesifik_hari)
    {
        $query = $this->db
            ->select('a.id_assignment, a.tipe, a.id_referensi, a.hari_ke, a.is_hari_kerja, a.berlaku_mulai, a.berlaku_selesai, a.keterangan,
                      s.id_shift, s.kode_shift, s.nama_shift, s.jam_masuk, s.jam_keluar, s.toleransi_menit,
                      s.lintas_tengah_malam, s.is_default, s.is_active')
            ->from(self::TABLE_ASSIGN . ' a')
            ->join(self::TABLE . ' s', 's.id_shift = a.id_shift', 'left')
            ->where('a.tipe', $tipe)
            ->where('a.id_referensi', (int) $id_referensi)
            ->where('a.berlaku_mulai <=', $tanggal)
            ->group_start()
                ->where('a.berlaku_selesai IS NULL', NULL, FALSE)
                ->or_where('a.berlaku_selesai >=', $tanggal)
            ->group_end()
            ->order_by('a.berlaku_mulai', 'DESC')
            ->order_by('a.id_assignment', 'DESC')
            ->limit(1);

        if ($is_spesifik_hari) {
            $query->where('a.hari_ke', (int) $hari_ke);
        } else {
            $query->group_start()
                  ->where('a.hari_ke', self::ALL_DAYS)
                  ->or_where('a.hari_ke IS NULL', NULL, FALSE)
                  ->group_end();
        }

        $result = $query->get()->row();
        if (!$result) {
            return NULL;
        }

        if ((int) $result->is_hari_kerja === 1 && (empty($result->id_shift) || (int) $result->is_active !== 1)) {
            return NULL;
        }

        return $result;
    }

    private function _build_default_schedule($tanggal)
    {
        $hari_ke = (int) date('N', strtotime($tanggal));
        if ($hari_ke > 5) {
            return (object) [
                'hari_ke'        => $hari_ke,
                'is_hari_kerja'  => 0,
                'id_shift'       => NULL,
                'nama_shift'     => 'OFF',
                'is_default'     => 0,
                'keterangan'     => 'Fallback akhir pekan',
            ];
        }

        $shift = $this->get_default();
        if (!$shift) {
            return NULL;
        }

        $shift->hari_ke = $hari_ke;
        $shift->is_hari_kerja = 1;
        return $shift;
    }

    // ----------------------------------------------------------------
    // HITUNG KETERLAMBATAN
    // Return array ['is_terlambat' => bool, 'menit_terlambat' => int]
    // ----------------------------------------------------------------

    /**
     * @param string $jam_masuk_aktual  format HH:MM atau HH:MM:SS
     * @param object $shift             object dari get_shift_for_pegawai()
     */
    public function hitung_keterlambatan($jam_masuk_aktual, $shift)
    {
        if (!$jam_masuk_aktual || !$shift) {
            return ['is_terlambat' => 0, 'menit_terlambat' => 0];
        }

        $batas_masuk  = strtotime($shift->jam_masuk) + ((int) $shift->toleransi_menit * 60);
        $aktual       = strtotime($jam_masuk_aktual);

        if ($aktual > $batas_masuk) {
            $selisih_detik = $aktual - strtotime($shift->jam_masuk);
            return [
                'is_terlambat'    => 1,
                'menit_terlambat' => (int) ceil($selisih_detik / 60),
            ];
        }

        return ['is_terlambat' => 0, 'menit_terlambat' => 0];
    }

    // ----------------------------------------------------------------
    // HITUNG LEMBUR OTOMATIS
    // Return jam lembur (float), atau 0 jika di bawah minimum
    // ----------------------------------------------------------------

    /**
     * @param string $jam_keluar_aktual  format HH:MM atau HH:MM:SS
     * @param object $shift
     * @param int    $minimum_menit      threshold minimum
     */
    public function hitung_lembur($jam_keluar_aktual, $shift, $minimum_menit = 30)
    {
        if (!$jam_keluar_aktual || !$shift) {
            return 0;
        }

        $keluar_aktual = strtotime($jam_keluar_aktual);
        $keluar_shift  = strtotime($shift->jam_keluar);

        // Shift lintas tengah malam: jam_keluar di hari berikutnya
        if ((int) $shift->lintas_tengah_malam && $keluar_aktual < $keluar_shift) {
            $keluar_aktual += 86400; // tambah 24 jam
        }

        $selisih_detik = $keluar_aktual - $keluar_shift;

        if ($selisih_detik >= ($minimum_menit * 60)) {
            return round($selisih_detik / 3600, 2);
        }

        return 0;
    }
}
