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

class Komponen extends Auth_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load->model('Komponen_model');
        $this->Komponen_model->ensure_default_components();
    }

    public function index()
    {
        $data = [
            'page_title'    => 'Komponen Gaji',
            'page_subtitle' => 'Kelola komponen tunjangan dan potongan gaji',
            'list'          => $this->Komponen_model->get_all(),
            'used_ids'      => $this->Komponen_model->get_used_ids(),
        ];
        $this->render('master/komponen/list', $data);
    }

    public function tambah()
    {
        $this->_set_rules();
        if ($this->form_validation->run() === FALSE) {
            $this->render('master/komponen/form', $this->_form_data());
            return;
        }

        $payload = $this->_payload();
        if ($this->Komponen_model->kode_exists($payload['kode_komponen'])) {
            $this->session->set_flashdata('error', 'Kode komponen sudah digunakan.');
            $this->render('master/komponen/form', $this->_form_data());
            return;
        }

        if ($payload['cara_hitung'] === 'FORMULA') {
            $dep_errors = $this->_check_formula_dependencies($payload['formula_expression'], $payload['urutan']);
            if (!empty($dep_errors)) {
                $this->session->set_flashdata('error', 'Urutan komponen tidak valid. Komponen berikut digunakan dalam rumus tetapi memiliki urutan ≥ ' . $payload['urutan'] . ': ' . implode(', ', $dep_errors) . '. Turunkan urutan komponen tersebut atau naikkan urutan komponen ini.');
                $this->render('master/komponen/form', $this->_form_data());
                return;
            }
        }

        $id = $this->Komponen_model->insert($payload);
        if ($payload['cara_hitung'] === 'PER_HARI') {
            $this->Komponen_model->save_rules($id, (array) $this->input->post('rule_status'));
        }
        audit_log('TAMBAH', 'Komponen Gaji', 'Kode: ' . $payload['kode_komponen']);
        $this->session->set_flashdata('success', 'Komponen gaji berhasil ditambahkan.');
        redirect('master/komponen');
    }

    public function edit($id)
    {
        $row = $this->Komponen_model->get_by_id($id);
        if (!$row) { show_404(); return; }

        $this->_set_rules();
        if ($this->form_validation->run() === FALSE) {
            $this->render('master/komponen/form', $this->_form_data($row));
            return;
        }

        $payload = $this->_payload();
        if ($this->Komponen_model->kode_exists($payload['kode_komponen'], $id)) {
            $this->session->set_flashdata('error', 'Kode komponen sudah digunakan.');
            $this->render('master/komponen/form', $this->_form_data($row));
            return;
        }

        if ($payload['cara_hitung'] === 'FORMULA') {
            $dep_errors = $this->_check_formula_dependencies($payload['formula_expression'], $payload['urutan'], $id);
            if (!empty($dep_errors)) {
                $this->session->set_flashdata('error', 'Urutan komponen tidak valid. Komponen berikut digunakan dalam rumus tetapi memiliki urutan ≥ ' . $payload['urutan'] . ': ' . implode(', ', $dep_errors) . '. Turunkan urutan komponen tersebut atau naikkan urutan komponen ini.');
                $this->render('master/komponen/form', $this->_form_data($row));
                return;
            }
        }

        $this->Komponen_model->update($id, $payload);
        $this->Komponen_model->save_rules(
            $id,
            $payload['cara_hitung'] === 'PER_HARI' ? (array) $this->input->post('rule_status') : []
        );
        audit_log('EDIT', 'Komponen Gaji', 'Kode: ' . $payload['kode_komponen']);
        $this->session->set_flashdata('success', 'Komponen gaji berhasil diperbarui.');
        redirect('master/komponen');
    }

    public function preview_formula()
    {
        if ($this->input->method(TRUE) !== 'POST') {
            show_404();
            return;
        }

        $this->load->model('Payroll_model');

        $expression = trim((string) $this->input->post('formula_expression', FALSE));
        $context = [
            'GAPOK' => (float) $this->input->post('sim_gapok'),
            'LEMBUR' => (float) $this->input->post('sim_lembur'),
            'MASA_KERJA_BULAN' => (float) $this->input->post('sim_masa_kerja_bulan'),
            'HARI_MASUK' => (float) $this->input->post('sim_hari_masuk'),
            'HARI_TERLAMBAT' => (float) $this->input->post('sim_hari_terlambat'),
            'HARI_SAKIT' => (float) $this->input->post('sim_hari_sakit'),
            'HARI_CUTI' => (float) $this->input->post('sim_hari_cuti'),
            'HARI_IZIN' => (float) $this->input->post('sim_hari_izin'),
            'HARI_ALPHA' => (float) $this->input->post('sim_hari_alpha'),
            'HARI_LIBUR' => (float) $this->input->post('sim_hari_libur'),
            'SUBTOTAL_TUNJANGAN' => (float) $this->input->post('sim_subtotal_tunjangan'),
            'SUBTOTAL_POTONGAN' => (float) $this->input->post('sim_subtotal_potongan'),
            'BRUTO_SEMENTARA' => (float) $this->input->post('sim_bruto_sementara'),
            'TJ_POSISI' => (float) $this->input->post('sim_tj_posisi'),
            'TJ_MAKAN' => (float) $this->input->post('sim_tj_makan'),
            'TJ_TRANS' => (float) $this->input->post('sim_tj_trans'),
        ];

        try {
            $result = $this->Payroll_model->preview_formula_expression($expression, $context);
            $payload = [
                'success' => true,
                'result' => round((float) $result, 2),
                'result_label' => 'Rp ' . rupiah($result),
            ];
        } catch (RuntimeException $e) {
            $payload = [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }

        $this->output
            ->set_content_type('application/json')
            ->set_output(json_encode($payload));
    }

    public function toggle($id)
    {
        $row = $this->Komponen_model->get_by_id($id);
        if (!$row) { show_404(); return; }
        if ($row->is_wajib) {
            $this->session->set_flashdata('error', 'Komponen wajib tidak dapat dinonaktifkan.');
            redirect('master/komponen');
            return;
        }
        $this->Komponen_model->toggle_active($id);
        audit_log('TOGGLE', 'Komponen Gaji', 'Kode: ' . $row->kode_komponen);
        $this->session->set_flashdata('success', 'Status komponen berhasil diubah.');
        redirect('master/komponen');
    }

    public function hapus($id)
    {
        $row = $this->Komponen_model->get_by_id((int) $id);
        if (!$row) { show_404(); return; }

        if ($row->is_wajib) {
            $this->session->set_flashdata('error', 'Komponen wajib tidak dapat dihapus.');
            redirect('master/komponen');
            return;
        }

        $used = $this->Komponen_model->get_used_ids();
        if (in_array((int) $id, $used)) {
            $this->session->set_flashdata('error', 'Komponen tidak dapat dihapus karena sudah digunakan dalam data payroll atau mapping gaji.');
            redirect('master/komponen');
            return;
        }

        $this->Komponen_model->delete((int) $id);
        audit_log('HAPUS', 'Komponen Gaji', 'Kode: ' . $row->kode_komponen);
        $this->session->set_flashdata('success', 'Komponen ' . $row->kode_komponen . ' berhasil dihapus.');
        redirect('master/komponen');
    }
    // -------------------------------------------------------
    private function _set_rules()
    {
        $cara_hitung = $this->input->post('cara_hitung', TRUE) ?: 'FLAT';

        $this->form_validation->set_rules('kode_komponen', 'Kode Komponen', 'required|trim|max_length[20]|alpha_dash');
        $this->form_validation->set_rules('nama_komponen', 'Nama Komponen', 'required|trim|max_length[100]');
        $this->form_validation->set_rules('jenis',         'Jenis',         'required|in_list[TUNJANGAN,POTONGAN]');
        $this->form_validation->set_rules('cara_hitung',   'Cara Hitung',   'required|in_list[FLAT,PER_HARI,FORMULA]');
        $this->form_validation->set_rules('nilai_default', 'Nilai Default', 'required|numeric');
        $this->form_validation->set_rules('urutan',        'Urutan',        'required|is_natural_no_zero');
        $this->form_validation->set_rules('tipe_periode',  'Tipe Periode',  'required|in_list[SEMUA,REGULER,THR]');
        if ($cara_hitung === 'FLAT') {
            $this->form_validation->set_rules('tipe_nilai', 'Tipe Nilai', 'required|in_list[NOMINAL,PERSEN]');
        }
        if ($cara_hitung === 'FORMULA') {
            $this->form_validation->set_rules('formula_expression', 'Rumus Formula', 'required|trim|max_length[1000]');
        }
    }

    private function _payload()
    {
        $cara_hitung = $this->input->post('cara_hitung', TRUE) ?: 'FLAT';

        return [
            'kode_komponen'      => $this->input->post('kode_komponen', TRUE),
            'nama_komponen'      => $this->input->post('nama_komponen', TRUE),
            'jenis'              => $this->input->post('jenis', TRUE),
            'tipe_nilai'         => $cara_hitung === 'FLAT' ? $this->input->post('tipe_nilai', TRUE) : 'NOMINAL',
            'cara_hitung'        => $cara_hitung,
            'nilai_default'      => (float) $this->input->post('nilai_default'),
            'formula_expression' => $cara_hitung === 'FORMULA'
                ? trim((string) $this->input->post('formula_expression', FALSE))
                : null,
            'urutan'             => (int) $this->input->post('urutan'),
            'tipe_periode'       => $this->input->post('tipe_periode', TRUE) ?: 'SEMUA',
        ];
    }

    private function _form_data($row = NULL)
    {
        $existing_rules = $row ? $this->Komponen_model->get_rules($row->id_komponen) : [];
        $input_urutan = (int) ($this->input->post('urutan') ?: ($row->urutan ?? 10));

        return [
            'page_title'                  => $row ? 'Edit Komponen Gaji' : 'Tambah Komponen Gaji',
            'page_subtitle'               => 'Komponen tunjangan dan potongan',
            'row'                         => $row,
            'existing_rules'              => $existing_rules,
            'formula_reference_components'=> $this->Komponen_model->get_formula_reference_components($row->id_komponen ?? NULL),
            'formula_current_urutan'      => $input_urutan,
        ];
    }

    /**
     * Periksa apakah semua komponen yang direferensikan dalam formula memiliki
     * urutan lebih kecil dari urutan komponen saat ini.
     * Mengembalikan array kode komponen bermasalah (urutan >= $current_urutan).
     */
    private function _check_formula_dependencies($formula, $current_urutan, $exclude_id = NULL)
    {
        preg_match_all('/\b([A-Z][A-Z0-9_]*)\b/', strtoupper((string) $formula), $matches);
        if (empty($matches[1])) return [];

        $system_vars = [
            'GAPOK','LEMBUR','MASA_KERJA_BULAN',
            'HARI_MASUK','HARI_TERLAMBAT','HARI_SAKIT','HARI_CUTI','HARI_IZIN','HARI_ALPHA','HARI_LIBUR',
            'SUBTOTAL_TUNJANGAN','SUBTOTAL_POTONGAN','BRUTO_SEMENTARA',
            'IF','MIN','MAX','ROUND','ABS',
        ];

        $komponen_vars = array_values(array_unique(array_diff($matches[1], $system_vars)));
        if (empty($komponen_vars)) return [];

        $this->db->where_in('kode_komponen', $komponen_vars);
        $this->db->where('urutan >=', (int) $current_urutan);
        if ($exclude_id) {
            $this->db->where('id_komponen !=', (int) $exclude_id);
        }
        $rows = $this->db->select('kode_komponen, urutan')->get('tb_komponen_gaji')->result();

        $errors = [];
        foreach ($rows as $r) {
            $errors[] = $r->kode_komponen . ' (urutan ' . $r->urutan . ')';
        }
        return $errors;
    }
}
