<?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\Response;
use App\Core\Settings;
use App\Services\ApprovalService;
use App\Services\ArCalculator;
use App\Services\ExportService;

/** Nota kredit, penyesuaian saldo, dan hapus buku piutang. */
class CreditNoteController extends Controller
{
    protected string $module = 'nota_kredit';

    public const TYPES = [
        'NOTA_KREDIT' => 'Nota Kredit',
        'PENYESUAIAN' => 'Penyesuaian Saldo',
        'HAPUS_BUKU'  => 'Hapus Buku (Bad Debt)',
        'DISKON'      => 'Diskon / Potongan Harga',
    ];

    private function filters(): array
    {
        $f = [];
        foreach (['cari', 'status', 'cn_type'] as $k) { $v = trim((string)Request::get($k, '')); if ($v !== '') $f[$k] = $v; }
        foreach (['dari', 'sampai'] as $k) { $d = Request::date($k); if ($d) $f[$k] = $d; }
        return $f;
    }

    public function index(): void
    {
        $f = $this->filters();
        [$page, $per] = $this->pageParams();
        $db = Database::i();
        $w = ['cn.deleted_at IS NULL'];
        $p = [];
        if (!empty($f['cari']))    { $w[] = '(cn.cn_no LIKE :q OR i.invoice_no LIKE :q OR c.name LIKE :q)'; $p['q'] = '%' . $f['cari'] . '%'; }
        if (!empty($f['status']))  { $w[] = 'cn.status = :st'; $p['st'] = $f['status']; }
        if (!empty($f['cn_type'])) { $w[] = 'cn.cn_type = :ct'; $p['ct'] = $f['cn_type']; }
        if (!empty($f['dari']))    { $w[] = 'cn.cn_date >= :d1'; $p['d1'] = $f['dari']; }
        if (!empty($f['sampai']))  { $w[] = 'cn.cn_date <= :d2'; $p['d2'] = $f['sampai']; }
        $where = implode(' AND ', $w);

        $m = new \App\Models\CreditNote();
        $this->view('nota_kredit/index', [
            'judul' => 'Nota Kredit & Penyesuaian',
            'subjudul' => 'Pengurangan saldo piutang di luar pembayaran',
            'p' => $m->paginate(
                'SELECT cn.*, i.invoice_no, i.net_amount, i.outstanding_amount, c.name AS customer_name,
                        u.name AS created_by_name, ua.name AS approved_by_name
                   FROM credit_notes cn
                   LEFT JOIN invoices i ON i.id = cn.invoice_id
                   LEFT JOIN customers c ON c.id = i.customer_id
                   LEFT JOIN users u ON u.id = cn.created_by
                   LEFT JOIN users ua ON ua.id = cn.approved_by
                  WHERE ' . $where . ' ORDER BY cn.cn_date DESC, cn.id DESC',
                $p, $page, $per,
                'SELECT COUNT(*) FROM credit_notes cn LEFT JOIN invoices i ON i.id = cn.invoice_id
                   LEFT JOIN customers c ON c.id = i.customer_id WHERE ' . $where),
            'filters' => $f, 'types' => self::TYPES,
            'stat' => $db->one("SELECT COUNT(*) total, SUM(status='pending') menunggu,
                    SUM(status='approved') disetujui,
                    COALESCE(SUM(CASE WHEN status='approved' THEN amount END),0) nilai_disetujui,
                    COALESCE(SUM(CASE WHEN status='pending' THEN amount END),0) nilai_menunggu
                    FROM credit_notes WHERE deleted_at IS NULL"),
        ]);
    }

    public function create(): void
    {
        $invoiceId = Request::int('invoice_id');
        $inv = $invoiceId > 0 ? (new \App\Models\Invoice())->findFull($invoiceId) : null;
        $this->view('nota_kredit/form', [
            'judul' => 'Tambah Nota Kredit / Penyesuaian',
            'types' => self::TYPES, 'inv' => $inv,
            'nomorSaran' => $this->nextNo(),
        ]);
    }

    private function nextNo(): string
    {
        $prefix = Settings::str('ar.prefix_nota_kredit', 'CN');
        $ym = date('ym');
        $last = Database::i()->value('SELECT cn_no FROM credit_notes WHERE cn_no LIKE :p ORDER BY cn_no DESC LIMIT 1',
                                     ['p' => "$prefix/$ym/%"]);
        $n = 1;
        if ($last && preg_match('#/(\d+)$#', (string)$last, $m)) $n = (int)$m[1] + 1;
        return sprintf('%s/%s/%04d', $prefix, $ym, $n);
    }

    public function store(): void
    {
        $this->validate([
            'invoice_id' => 'required|integer',
            'cn_date' => 'required|date',
            'amount' => 'required|numeric|min:0.01',
            'reason' => 'required|minlen:5',
        ], ['invoice_id' => 'Invoice', 'cn_date' => 'Tanggal', 'amount' => 'Nilai', 'reason' => 'Alasan'],
           '/nota-kredit/tambah');

        $db = Database::i();
        $invoiceId = Request::int('invoice_id');
        $inv = $db->one('SELECT * FROM invoices WHERE id = ? AND deleted_at IS NULL', [$invoiceId]);
        if (!$inv) { Flash::error('Invoice tidak ditemukan.'); $this->redirect('/nota-kredit/tambah'); }

        $amount = Request::money('amount');
        $type = in_array(Request::post('cn_type'), array_keys(self::TYPES), true) ? (string)Request::post('cn_type') : 'NOTA_KREDIT';
        if ($amount > (float)$inv['outstanding_amount'] + 0.5 && $type !== 'PENYESUAIAN') {
            Flash::error('Nilai nota kredit (' . rp($amount) . ') melebihi saldo piutang invoice ('
                . rp((float)$inv['outstanding_amount']) . ').');
            $this->redirect('/nota-kredit/tambah');
        }

        $id = $db->insert('credit_notes', [
            'cn_no' => $this->nextNo(),
            'cn_date' => Request::date('cn_date'),
            'invoice_id' => $invoiceId,
            'cn_type' => $type,
            'amount' => $amount,
            'reason' => mb_substr((string)Request::post('reason', ''), 0, 500),
            'status' => 'pending',
            'created_by' => Auth::id() ?: null,
        ]);

        $action = $type === 'HAPUS_BUKU' ? 'approve' : 'approve';
        $module = $type === 'HAPUS_BUKU' ? 'hapus_buku' : 'nota_kredit';
        ApprovalService::request($module, $action, 'credit_note', $id,
            self::TYPES[$type] . ' ' . rp($amount) . ' untuk invoice ' . $inv['invoice_no'], $amount,
            ['invoice_no' => $inv['invoice_no'], 'alasan' => Request::post('reason')]);

        Audit::log('nota_kredit', 'create', 'credit_note', $id,
            self::TYPES[$type] . ' dibuat untuk invoice ' . $inv['invoice_no'] . ' senilai ' . rp($amount));
        Flash::success('Nota kredit berhasil dibuat dan menunggu persetujuan. Saldo invoice belum berubah sampai disetujui.');
        $this->redirect('/nota-kredit');
    }

    public function approve(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $db = Database::i();
        $cn = $db->one('SELECT * FROM credit_notes WHERE id = :id AND deleted_at IS NULL', ['id' => $id]);
        if (!$cn) Response::abort(404, 'Nota kredit tidak ditemukan.');
        if ($cn['status'] === 'approved') { Flash::warning('Nota kredit ini sudah disetujui.'); $this->back('/nota-kredit'); }

        $db->transaction(function () use ($db, $cn, $id) {
            $db->update('credit_notes', ['status' => 'approved', 'approved_by' => Auth::id() ?: null,
                'approved_at' => Dates::now()], 'id = :pk', ['pk' => $id]);
            if ($cn['cn_type'] === 'HAPUS_BUKU') {
                $db->update('invoices', ['doc_status' => 'written_off', 'is_revenue_counted' => 0],
                            'id = :pk', ['pk' => (int)$cn['invoice_id']]);
            }
            ArCalculator::recalculate((int)$cn['invoice_id']);
        });
        Audit::log('nota_kredit', 'approve', 'credit_note', $id, 'Nota kredit ' . $cn['cn_no'] . ' disetujui');
        Flash::success('Nota kredit disetujui dan saldo piutang invoice telah dihitung ulang.');
        $this->back('/nota-kredit');
    }

    public function reject(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $alasan = trim((string)Request::post('alasan', ''));
        Database::i()->update('credit_notes', ['status' => 'rejected'], 'id = :pk', ['pk' => $id]);
        Audit::log('nota_kredit', 'reject', 'credit_note', $id, 'Nota kredit ditolak. ' . $alasan);
        Flash::success('Nota kredit ditolak.');
        $this->back('/nota-kredit');
    }

    public function destroy(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $db = Database::i();
        $cn = $db->one('SELECT * FROM credit_notes WHERE id = :id', ['id' => $id]);
        if (!$cn) Response::abort(404, 'Nota kredit tidak ditemukan.');
        $db->exec('UPDATE credit_notes SET deleted_at = NOW() WHERE id = ?', [$id]);
        ArCalculator::recalculate((int)$cn['invoice_id']);
        Audit::log('nota_kredit', 'delete', 'credit_note', $id, 'Nota kredit ' . $cn['cn_no'] . ' dihapus', $cn, null);
        Flash::success('Nota kredit dihapus dan saldo invoice dihitung ulang.');
        $this->back('/nota-kredit');
    }

    public function export(): void
    {
        $f = $this->filters();
        $rows = Database::i()->all(
            'SELECT cn.*, i.invoice_no, c.name AS customer_name, u.name AS created_by_name, ua.name AS approved_by_name
               FROM credit_notes cn
               LEFT JOIN invoices i ON i.id = cn.invoice_id
               LEFT JOIN customers c ON c.id = i.customer_id
               LEFT JOIN users u ON u.id = cn.created_by
               LEFT JOIN users ua ON ua.id = cn.approved_by
              WHERE cn.deleted_at IS NULL ORDER BY cn.cn_date DESC LIMIT '
            . Settings::int('laporan.maks_baris_export', 50000)
        );
        ExportService::send((string)Request::get('format', 'xlsx'), [
            'judul' => 'LAPORAN NOTA KREDIT & PENYESUAIAN',
            'tanggal_lapor' => Dates::fmt(Dates::today()),
            'nama_berkas' => 'nota_kredit', 'sheet' => 'Nota Kredit', 'orientasi' => 'landscape',
        ], [
            ['label' => 'No. Nota Kredit', 'key' => 'cn_no',           'type' => 'text'],
            ['label' => 'Tanggal',         'key' => 'cn_date',         'type' => 'date'],
            ['label' => 'Jenis',           'key' => 'cn_type',         'type' => 'text'],
            ['label' => 'No. Invoice',     'key' => 'invoice_no',      'type' => 'text'],
            ['label' => 'Customer',        'key' => 'customer_name',   'type' => 'text'],
            ['label' => 'Nilai',           'key' => 'amount',          'type' => 'money'],
            ['label' => 'Status',          'key' => 'status',          'type' => 'text'],
            ['label' => 'Alasan',          'key' => 'reason',          'type' => 'text', 'width' => 55, 'pdf_width' => 3],
            ['label' => 'Dibuat Oleh',     'key' => 'created_by_name', 'type' => 'text'],
            ['label' => 'Disetujui Oleh',  'key' => 'approved_by_name','type' => 'text'],
        ], $rows, ['cn_no' => 'TOTAL', 'amount' => array_sum(array_map(fn($r) => (float)$r['amount'], $rows))]);
    }
}
