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

class Komponen_model extends CI_Model
{
    const TABLE      = 'tb_komponen_gaji';
    const TABLE_RULE = 'tb_komponen_kehadiran_rule';

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

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

    public function kode_exists($kode, $exclude_id = NULL)
    {
        if ($exclude_id) $this->db->where('id_komponen !=', $exclude_id);
        return $this->db->where('kode_komponen', strtoupper($kode))->count_all_results(self::TABLE) > 0;
    }

    public function insert($data)
    {
        $data['kode_komponen'] = strtoupper($data['kode_komponen']);
        $this->db->insert(self::TABLE, $data);
        return $this->db->insert_id();
    }

    /**
     * Ensure default statutory components are available.
     * Will not overwrite existing components with same kode_komponen.
     */
    public function ensure_default_components()
    {
        $presets = [
            [
                'kode_komponen' => 'THR',
                'nama_komponen' => 'Tunjangan Hari Raya (THR)',
                'jenis' => 'TUNJANGAN',
                'tipe_nilai' => 'NOMINAL',
                'cara_hitung' => 'FORMULA',
                'nilai_default' => 0,
                'formula_expression' => 'ROUND((GAPOK + TJ_POSISI) / 12 * MIN(MASA_KERJA_BULAN, 12), 0)',
                'urutan' => 50,
                'tipe_periode' => 'THR',
                'is_wajib' => 0,
                'is_active' => 1,
            ],
            [
                'kode_komponen' => 'BPJS_KES_KRY',
                'nama_komponen' => 'BPJS Kesehatan Karyawan (1%)',
                'jenis' => 'POTONGAN',
                'tipe_nilai' => 'NOMINAL',
                'cara_hitung' => 'FORMULA',
                'nilai_default' => 0,
                'formula_expression' => 'ROUND(GAPOK * 0.01, 0)',
                'urutan' => 60,
                'tipe_periode' => 'REGULER',
                'is_wajib' => 0,
                'is_active' => 1,
            ],
            [
                'kode_komponen' => 'BPJS_KES_PRS',
                'nama_komponen' => 'BPJS Kesehatan Perusahaan (4%)',
                'jenis' => 'TUNJANGAN',
                'tipe_nilai' => 'NOMINAL',
                'cara_hitung' => 'FORMULA',
                'nilai_default' => 0,
                'formula_expression' => 'ROUND(GAPOK * 0.04, 0)',
                'urutan' => 61,
                'tipe_periode' => 'REGULER',
                'is_wajib' => 0,
                'is_active' => 1,
            ],
            [
                'kode_komponen' => 'BPJS_JHT_KRY',
                'nama_komponen' => 'BPJS JHT Karyawan (2%)',
                'jenis' => 'POTONGAN',
                'tipe_nilai' => 'NOMINAL',
                'cara_hitung' => 'FORMULA',
                'nilai_default' => 0,
                'formula_expression' => 'ROUND(GAPOK * 0.02, 0)',
                'urutan' => 62,
                'tipe_periode' => 'REGULER',
                'is_wajib' => 0,
                'is_active' => 1,
            ],
            [
                'kode_komponen' => 'BPJS_JHT_PRS',
                'nama_komponen' => 'BPJS JHT Perusahaan (3.7%)',
                'jenis' => 'TUNJANGAN',
                'tipe_nilai' => 'NOMINAL',
                'cara_hitung' => 'FORMULA',
                'nilai_default' => 0,
                'formula_expression' => 'ROUND(GAPOK * 0.037, 0)',
                'urutan' => 63,
                'tipe_periode' => 'REGULER',
                'is_wajib' => 0,
                'is_active' => 1,
            ],
            [
                'kode_komponen' => 'BPJS_JP_KRY',
                'nama_komponen' => 'BPJS Pensiun Karyawan (1%)',
                'jenis' => 'POTONGAN',
                'tipe_nilai' => 'NOMINAL',
                'cara_hitung' => 'FORMULA',
                'nilai_default' => 0,
                'formula_expression' => 'ROUND(GAPOK * 0.01, 0)',
                'urutan' => 64,
                'tipe_periode' => 'REGULER',
                'is_wajib' => 0,
                'is_active' => 1,
            ],
            [
                'kode_komponen' => 'BPJS_JP_PRS',
                'nama_komponen' => 'BPJS Pensiun Perusahaan (2%)',
                'jenis' => 'TUNJANGAN',
                'tipe_nilai' => 'NOMINAL',
                'cara_hitung' => 'FORMULA',
                'nilai_default' => 0,
                'formula_expression' => 'ROUND(GAPOK * 0.02, 0)',
                'urutan' => 65,
                'tipe_periode' => 'REGULER',
                'is_wajib' => 0,
                'is_active' => 1,
            ],
        ];

        $codes = array_column($presets, 'kode_komponen');
        $existing_rows = $this->db
            ->select('kode_komponen')
            ->where_in('kode_komponen', $codes)
            ->get(self::TABLE)
            ->result_array();

        $existing = [];
        foreach ($existing_rows as $row) {
            $existing[strtoupper((string) $row['kode_komponen'])] = true;
        }

        foreach ($presets as $preset) {
            if (isset($existing[$preset['kode_komponen']])) {
                continue;
            }
            $this->db->insert(self::TABLE, $preset);
        }
    }

    public function update($id, $data)
    {
        $data['kode_komponen'] = strtoupper($data['kode_komponen']);
        $this->db->where('id_komponen', $id)->update(self::TABLE, $data);
    }

    public function toggle_active($id)
    {
        $row = $this->get_by_id($id);
        if (!$row) return FALSE;
        $this->db->where('id_komponen', $id)->update(self::TABLE, ['is_active' => $row->is_active ? 0 : 1]);
        return TRUE;
    }

    public function get_formula_reference_components($exclude_id = NULL)
    {
        if ($exclude_id) {
            $this->db->where('id_komponen !=', (int) $exclude_id);
        }

        return $this->db
            ->select('id_komponen, kode_komponen, nama_komponen, urutan')
            ->where('is_active', 1)
            ->order_by('urutan', 'ASC')
            ->get(self::TABLE)
            ->result_array();
    }

    // -------------------------------------------------------
    // Kehadiran rules
    // -------------------------------------------------------

    /**
     * Returns array of status_absensi strings for the given komponen.
     * e.g. ['MASUK', 'TERLAMBAT']
     */
    public function get_rules($id_komponen)
    {
        $rows = $this->db
            ->select('status_absensi')
            ->where('id_komponen', (int) $id_komponen)
            ->get(self::TABLE_RULE)->result();
        return array_column(array_map(function($r){ return (array)$r; }, $rows), 'status_absensi');
    }

    /**
     * Returns [id_komponen => [status_absensi, ...]] for ALL komponen.
     * Used by payroll engine to avoid N+1 queries.
     */
    public function get_all_rules_map()
    {
        $rows = $this->db->get(self::TABLE_RULE)->result();
        $map = [];
        foreach ($rows as $r) {
            $map[(int) $r->id_komponen][] = $r->status_absensi;
        }
        return $map;
    }

    /**
     * Replace rules for a komponen.
     * @param int   $id_komponen
     * @param array $statuses  e.g. ['MASUK','TERLAMBAT']
     */
    public function save_rules($id_komponen, array $statuses)
    {
        $this->db->where('id_komponen', (int) $id_komponen)->delete(self::TABLE_RULE);
        foreach ($statuses as $st) {
            $st = strtoupper(trim((string) $st));
            if ($st === '') continue;
            $this->db->insert(self::TABLE_RULE, [
                'id_komponen'    => (int) $id_komponen,
                'status_absensi' => $st,
            ]);
        }
    }

    /**
     * Returns array of id_komponen (int) that have been used in payroll detail
     * or mapped to an employee. These cannot be deleted.
     */
    public function get_used_ids()
    {
        $payroll = $this->db->select('id_komponen')->distinct()->get('tb_payroll_komponen')->result();
        $mapping = $this->db->select('id_komponen')->distinct()->get('tb_pegawai_komponen')->result();
        $ids = [];
        foreach ($payroll as $r) $ids[] = (int) $r->id_komponen;
        foreach ($mapping as $r) $ids[] = (int) $r->id_komponen;
        return array_values(array_unique($ids));
    }

    public function delete($id)
    {
        $this->db->where('id_komponen', (int) $id)->delete(self::TABLE_RULE);
        $this->db->where('id_komponen', (int) $id)->delete(self::TABLE);
        return $this->db->affected_rows() > 0;
    }
}
