<?php
namespace App\Controllers;

use App\Core\Audit;
use App\Core\Auth;
use App\Core\Config;
use App\Core\Controller;
use App\Core\Database;
use App\Core\Flash;
use App\Core\Request;
use App\Core\Response;
use App\Core\Settings;

/** Unggah & unduh lampiran dokumen. */
class AttachmentController extends Controller
{
    protected string $module = 'lampiran';

    /** Ekstensi yang selalu ditolak apa pun pengaturannya. */
    private const BLOCKED = ['php', 'php3', 'php4', 'php5', 'php7', 'php8', 'phtml', 'phar', 'phps',
                             'exe', 'com', 'bat', 'cmd', 'sh', 'bash', 'cgi', 'pl', 'py', 'jsp',
                             'asp', 'aspx', 'js', 'mjs', 'html', 'htm', 'htaccess', 'svg'];

    public function upload(): void
    {
        $entityType = (string)Request::post('entity_type', 'invoice');
        $entityId = Request::int('entity_id');
        if (!in_array($entityType, ['invoice', 'payment', 'invoice_revision', 'collection_followup', 'credit_note', 'customer'], true)
            || $entityId <= 0) {
            Flash::error('Entitas lampiran tidak valid.');
            $this->back('/');
        }
        $f = Request::file('berkas');
        if (!$f) { Flash::error('Tidak ada berkas yang dipilih.'); $this->back('/'); }
        if ($f['error'] !== UPLOAD_ERR_OK) { Flash::error('Berkas gagal diunggah (kode ' . $f['error'] . ').'); $this->back('/'); }

        $maxMb = Settings::int('keamanan.maks_ukuran_lampiran_mb', 10);
        if ((int)$f['size'] > $maxMb * 1048576) {
            Flash::error('Ukuran berkas melebihi batas ' . $maxMb . ' MB.');
            $this->back('/');
        }

        $orig = (string)$f['name'];
        $ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
        if ($ext === '' || in_array($ext, self::BLOCKED, true)) {
            Flash::error('Jenis berkas "' . $ext . '" tidak diizinkan karena berpotensi dieksekusi di server.');
            $this->back('/');
        }
        $allowed = array_map('trim', explode(',', Settings::str('keamanan.ekstensi_lampiran_diizinkan', 'pdf,jpg,jpeg,png,xlsx,docx')));
        if (!in_array($ext, $allowed, true)) {
            Flash::error('Ekstensi berkas tidak diizinkan. Yang diperbolehkan: ' . implode(', ', $allowed) . '.');
            $this->back('/');
        }
        // Verifikasi MIME sebenarnya
        $mime = function_exists('mime_content_type') ? (mime_content_type($f['tmp_name']) ?: '') : '';
        if ($mime !== '' && preg_match('#(php|x-httpd|x-sh|x-executable|javascript)#i', $mime)) {
            Flash::error('Isi berkas terdeteksi sebagai berkas yang dapat dieksekusi dan ditolak.');
            $this->back('/');
        }

        $dir = rtrim((string)Config::get('paths.uploads'), '/') . '/' . $entityType . '/' . date('Y/m');
        if (!is_dir($dir)) @mkdir($dir, 0775, true);
        $stored = bin2hex(random_bytes(16)) . '.' . $ext;
        $path = $dir . '/' . $stored;
        if (!move_uploaded_file($f['tmp_name'], $path)) {
            Flash::error('Berkas gagal disimpan di server.');
            $this->back('/');
        }
        @chmod($path, 0644);

        $id = Database::i()->insert('attachments', [
            'entity_type' => $entityType, 'entity_id' => $entityId,
            'attachment_type_id' => Request::int('attachment_type_id') ?: null,
            'file_name' => mb_substr($orig, 0, 255),
            'stored_name' => $entityType . '/' . date('Y/m') . '/' . $stored,
            'mime_type' => mb_substr($mime, 0, 120) ?: null,
            'file_size' => (int)$f['size'],
            'checksum' => hash_file('sha256', $path),
            'description' => mb_substr((string)Request::post('description', ''), 0, 400) ?: null,
            'uploaded_by' => Auth::id() ?: null,
        ]);
        Audit::log('lampiran', 'create', $entityType, $entityId, 'Lampiran diunggah: ' . $orig);
        Flash::success('Lampiran "' . $orig . '" berhasil diunggah.');
        $this->back('/');
    }

    public function download(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $a = Database::i()->one('SELECT * FROM attachments WHERE id = :id AND deleted_at IS NULL', ['id' => $id]);
        if (!$a) Response::abort(404, 'Lampiran tidak ditemukan.');
        $path = rtrim((string)Config::get('paths.uploads'), '/') . '/' . $a['stored_name'];
        if (!is_file($path)) Response::abort(404, 'Berkas lampiran tidak tersedia di server.');
        Audit::log('lampiran', 'export', $a['entity_type'], (int)$a['entity_id'], 'Lampiran diunduh: ' . $a['file_name']);
        Response::download($path, $a['file_name'], $a['mime_type'] ?: null);
    }

    public function destroy(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $a = Database::i()->one('SELECT * FROM attachments WHERE id = :id AND deleted_at IS NULL', ['id' => $id]);
        if (!$a) Response::abort(404, 'Lampiran tidak ditemukan.');
        Database::i()->exec('UPDATE attachments SET deleted_at = NOW() WHERE id = ?', [$id]);
        Audit::log('lampiran', 'delete', $a['entity_type'], (int)$a['entity_id'], 'Lampiran dihapus: ' . $a['file_name']);
        Flash::success('Lampiran dihapus.');
        $this->back('/');
    }
}
