<?php
namespace App\Controllers;

use App\Core\Audit;
use App\Core\Auth;
use App\Core\Controller;
use App\Core\Database;
use App\Core\Dates;
use App\Core\Flash;
use App\Core\Num;
use App\Core\Request;
use App\Core\Settings;
use App\Services\AgingService;
use App\Services\ArCalculator;

/** Pengaturan aplikasi & konfigurasi aging. */
class SettingController extends Controller
{
    protected string $module = 'pengaturan';

    private const GROUP_LABELS = [
        'umum' => ['Umum & Identitas Perusahaan', 'building'],
        'format' => ['Format Angka & Tanggal', '123'],
        'pajak' => ['Perpajakan (PPN & PPh 23)', 'receipt-cutoff'],
        'ar' => ['Aturan Piutang', 'cash-stack'],
        'aging' => ['Aging Piutang', 'hourglass-split'],
        'acp' => ['Average Collection Period', 'speedometer2'],
        'penagihan' => ['Penagihan', 'telephone-outbound'],
        'keamanan' => ['Keamanan', 'shield-lock'],
        'import' => ['Import Data', 'file-earmark-excel'],
        'laporan' => ['Laporan & Export', 'file-earmark-bar-graph'],
        'dashboard' => ['Dashboard', 'speedometer'],
        'backup' => ['Pencadangan', 'hdd'],
    ];

    public function index(): void
    {
        Settings::load(true);
        $groups = [];
        foreach (Settings::groups() as $g) {
            $groups[$g] = [
                'label' => self::GROUP_LABELS[$g][0] ?? ucfirst($g),
                'icon' => self::GROUP_LABELS[$g][1] ?? 'gear',
                'items' => Settings::group($g),
            ];
        }
        $this->view('pengaturan/index', [
            'judul' => 'Pengaturan',
            'subjudul' => 'Konfigurasi aturan bisnis, format, keamanan, dan integrasi',
            'groups' => $groups,
            'aktif' => (string)Request::get('grup', 'umum'),
            'info' => [
                'php' => PHP_VERSION,
                'db' => Database::i()->value('SELECT VERSION()'),
                'zip' => class_exists('\ZipArchive') ? 'Tersedia' : 'Tidak tersedia',
                'composer' => is_file(SIPIUTANG_ROOT . '/vendor/autoload.php') ? 'Terpasang' : 'Tidak dipakai (mode nol dependensi)',
                'phpspreadsheet' => class_exists('\PhpOffice\PhpSpreadsheet\Spreadsheet') ? 'Terpasang' : 'Tidak terpasang (memakai pembaca internal)',
                'dompdf' => class_exists('\Dompdf\Dompdf') ? 'Terpasang' : 'Tidak terpasang (memakai generator internal)',
                'upload_max' => ini_get('upload_max_filesize'),
                'memory' => ini_get('memory_limit'),
                'timezone' => date_default_timezone_get(),
                'waktu_server' => Dates::now(),
            ],
        ]);
    }

    public function save(): void
    {
        $group = (string)Request::post('grup', '');
        $items = Settings::group($group);
        $changed = 0;
        $old = []; $new = [];
        foreach ($items as $it) {
            $key = $it['setting_key'];
            $type = $it['value_type'];
            if ($type === 'bool') {
                $val = Request::bool('s_' . str_replace('.', '__', $key)) ? '1' : '0';
            } else {
                $raw = Request::post('s_' . str_replace('.', '__', $key), null);
                if ($raw === null) continue;
                $val = match ($type) {
                    'int' => (string)(int)Num::parse((string)$raw),
                    'decimal' => (string)Num::parse((string)$raw),
                    default => trim((string)$raw),
                };
            }
            if ((string)$it['setting_value'] !== $val) {
                $old[$key] = $it['setting_value'];
                $new[$key] = $val;
                Settings::set($key, $val, Auth::id() ?: null);
                $changed++;
            }
        }
        if ($changed > 0) {
            Audit::log('pengaturan', 'settings', 'application_settings', null,
                "Pengaturan kelompok \"$group\" diubah ($changed nilai)", $old, $new);
            Settings::load(true);
        }
        Flash::success($changed > 0
            ? "$changed pengaturan berhasil disimpan."
            : 'Tidak ada perubahan pengaturan yang disimpan.');
        $this->redirect('/pengaturan?grup=' . urlencode($group));
    }

    public function aging(): void
    {
        $db = Database::i();
        $configs = $db->all('SELECT * FROM aging_configurations WHERE deleted_at IS NULL ORDER BY is_default DESC, name');
        foreach ($configs as &$c) {
            $c['buckets'] = $db->all('SELECT * FROM aging_buckets WHERE aging_configuration_id = :id ORDER BY sort_order', ['id' => (int)$c['id']]);
        }
        unset($c);
        $this->view('pengaturan/aging', [
            'judul' => 'Konfigurasi Aging Piutang',
            'subjudul' => 'Atur bucket umur piutang sesuai kebutuhan pelaporan',
            'configs' => $configs,
            'defaultId' => Settings::int('aging.konfigurasi_default', 1),
        ]);
    }

    public function saveAging(): void
    {
        $db = Database::i();
        $id = Request::int('id') ?: null;
        $data = [
            'code' => strtoupper(preg_replace('/[^A-Za-z0-9_]/', '_', (string)Request::post('code', '')) ?: 'AGING_' . time()),
            'name' => mb_substr(trim((string)Request::post('name', '')), 0, 150),
            'basis' => in_array(Request::post('basis'), ['outstanding', 'net', 'gross'], true) ? (string)Request::post('basis') : 'outstanding',
            'reference' => in_array(Request::post('reference'), ['due_date', 'invoice_date', 'sent_date'], true) ? (string)Request::post('reference') : 'due_date',
            'include_paid' => Request::bool('include_paid') ? 1 : 0,
            'is_active' => 1,
        ];
        if ($data['name'] === '') { Flash::error('Nama konfigurasi wajib diisi.'); $this->back('/pengaturan/aging'); }

        $labels = Request::array('bucket_label');
        $froms  = Request::array('bucket_from');
        $tos    = Request::array('bucket_to');
        $colors = Request::array('bucket_color');
        $buckets = [];
        foreach ($labels as $i => $lbl) {
            $lbl = trim((string)$lbl);
            if ($lbl === '') continue;
            $to = trim((string)($tos[$i] ?? ''));
            $buckets[] = [
                'label' => mb_substr($lbl, 0, 80),
                'from_days' => (int)Num::parse((string)($froms[$i] ?? 0)),
                'to_days' => $to === '' ? null : (int)Num::parse($to),
                'color' => in_array($colors[$i] ?? '', ['primary', 'success', 'warning', 'danger', 'info', 'secondary', 'dark'], true)
                           ? (string)$colors[$i] : 'secondary',
                'sort_order' => count($buckets) + 1,
            ];
        }
        if (!$buckets) { Flash::error('Tambahkan minimal satu bucket aging.'); $this->back('/pengaturan/aging'); }

        $db->transaction(function () use ($db, $id, $data, $buckets) {
            if ($id) {
                $db->update('aging_configurations', $data, 'id = :pk', ['pk' => $id]);
                $db->exec('DELETE FROM aging_buckets WHERE aging_configuration_id = ?', [$id]);
            } else {
                $data['created_by'] = Auth::id() ?: null;
                $id = $db->insert('aging_configurations', $data);
            }
            foreach ($buckets as $b) {
                $db->insert('aging_buckets', array_merge($b, ['aging_configuration_id' => $id]));
            }
            if (Request::bool('set_default')) {
                $db->exec('UPDATE aging_configurations SET is_default = 0');
                $db->exec('UPDATE aging_configurations SET is_default = 1 WHERE id = ?', [$id]);
                Settings::set('aging.konfigurasi_default', (string)$id, Auth::id() ?: null);
            }
            return $id;
        });
        Audit::log('pengaturan', 'update', 'aging_configuration', $id, 'Konfigurasi aging disimpan: ' . $data['name']);
        Flash::success('Konfigurasi aging "' . $data['name'] . '" berhasil disimpan dengan ' . count($buckets) . ' bucket.');
        $this->redirect('/pengaturan/aging');
    }

    public function deleteAging(): void
    {
        $id = Request::int('id');
        $db = Database::i();
        $c = $db->one('SELECT * FROM aging_configurations WHERE id = :id', ['id' => $id]);
        if (!$c) { Flash::error('Konfigurasi tidak ditemukan.'); $this->back('/pengaturan/aging'); }
        if ((int)$c['is_default'] === 1) { Flash::error('Konfigurasi default tidak dapat dihapus. Tetapkan konfigurasi lain sebagai default terlebih dahulu.'); $this->back('/pengaturan/aging'); }
        $db->exec('UPDATE aging_configurations SET deleted_at = NOW() WHERE id = ?', [$id]);
        Audit::log('pengaturan', 'delete', 'aging_configuration', $id, 'Konfigurasi aging dihapus: ' . $c['name']);
        Flash::success('Konfigurasi aging dihapus.');
        $this->redirect('/pengaturan/aging');
    }

    /** Menghitung ulang seluruh nilai turunan piutang. */
    public function recalculate(): void
    {
        @set_time_limit(1800);
        $recomputeDue = Request::bool('hitung_ulang_jatuh_tempo');
        $asOf = Request::date('tanggal_lapor') ?: Dates::today();
        $t0 = microtime(true);
        $n = ArCalculator::recalculateBatch(null, $asOf, $recomputeDue);
        $sec = round(microtime(true) - $t0, 1);
        Audit::log('pengaturan', 'recalculate', null, null,
            "Hitung ulang piutang: $n invoice, tanggal pelaporan $asOf, jatuh tempo "
            . ($recomputeDue ? 'ikut dihitung ulang' : 'tidak diubah'));
        Flash::success("Hitung ulang selesai: " . nf($n) . " invoice diproses dalam {$sec} detik"
            . ($recomputeDue ? ', termasuk perhitungan ulang tanggal jatuh tempo.' : '.'));
        $this->redirect('/pengaturan');
    }
}
