<?php
namespace App\Controllers;

use App\Core\Acl;
use App\Core\Audit;
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\CollectionService;
use App\Services\DashboardService;
use App\Services\ExportService;

/** Modul penagihan: worklist, follow-up, janji bayar. */
class CollectionController extends Controller
{
    protected string $module = 'penagihan';

    public function index(): void
    {
        $asOf = Request::date('tanggal_lapor') ?: Dates::today();
        $kategori = (string)Request::get('kategori', 'terlambat');
        if (!isset(CollectionService::worklistCategories()[$kategori])) $kategori = 'terlambat';
        $filters = PiutangController::readFilters(false);
        [$page, $per] = $this->pageParams();

        $this->view('penagihan/index', [
            'judul' => 'Worklist Penagihan',
            'subjudul' => 'Daftar kerja penagihan per ' . Dates::fmtLong($asOf),
            'kategori' => $kategori, 'asOf' => $asOf,
            'counts' => CollectionService::worklistCounts($filters, $asOf),
            'p' => CollectionService::worklist($kategori, $filters, $asOf,
                    (string)Request::get('urut', 'outstanding_amount'), (string)Request::get('arah', 'desc'), $page, $per),
            'filters' => $filters, 'opsi' => DashboardService::filterOptions(),
            'statuses' => Database::i()->all('SELECT * FROM collection_statuses WHERE deleted_at IS NULL AND is_active=1 ORDER BY sort_order'),
            'officers' => Database::i()->all('SELECT id,name FROM collection_officers WHERE deleted_at IS NULL AND is_active=1 ORDER BY name'),
            'methods' => CollectionService::METHODS,
            'priorities' => CollectionService::PRIORITIES,
        ]);
    }

    public function history(): void
    {
        $f = [];
        foreach (['cari', 'method', 'priority'] as $k) { $v = trim((string)Request::get($k, '')); if ($v !== '') $f[$k] = $v; }
        foreach (['collection_officer_id', 'collection_status_id'] as $k) { $v = Request::int($k); if ($v > 0) $f[$k] = $v; }
        foreach (['dari', 'sampai'] as $k) { $v = Request::date($k); if ($v) $f[$k] = $v; }
        [$page, $per] = $this->pageParams();

        $this->view('penagihan/riwayat', [
            'judul' => 'Riwayat Follow-up Penagihan',
            'subjudul' => 'Seluruh aktivitas penagihan yang tercatat',
            'p' => CollectionService::followupListing($f, $page, $per), 'filters' => $f,
            'statuses' => Database::i()->all('SELECT * FROM collection_statuses WHERE deleted_at IS NULL ORDER BY sort_order'),
            'officers' => Database::i()->all('SELECT id,name FROM collection_officers WHERE deleted_at IS NULL ORDER BY name'),
            'methods' => CollectionService::METHODS,
        ]);
    }

    public function promises(): void
    {
        $f = [];
        foreach (['status', 'cari'] as $k) { $v = trim((string)Request::get($k, '')); if ($v !== '') $f[$k] = $v; }
        foreach (['dari', 'sampai'] as $k) { $v = Request::date($k); if ($v) $f[$k] = $v; }
        [$page, $per] = $this->pageParams();
        $db = Database::i();
        $this->view('penagihan/janji_bayar', [
            'judul' => 'Janji Bayar (Promise to Pay)',
            'subjudul' => 'Komitmen pembayaran dari customer beserta realisasinya',
            'p' => CollectionService::promiseListing($f, $page, $per), 'filters' => $f,
            'stat' => $db->one("SELECT COUNT(*) total,
                SUM(status='TERBUKA') terbuka, SUM(status='DIPENUHI') dipenuhi,
                SUM(status='SEBAGIAN') sebagian, SUM(status='GAGAL') gagal,
                COALESCE(SUM(CASE WHEN status='TERBUKA' THEN promise_amount END),0) nilai_terbuka,
                COALESCE(SUM(CASE WHEN status='GAGAL' THEN promise_amount END),0) nilai_gagal
                FROM promise_to_pay WHERE deleted_at IS NULL"),
        ]);
    }

    public function create(): void
    {
        $invoiceId = Request::int('invoice_id');
        $inv = $invoiceId > 0 ? Database::i()->one(
            'SELECT i.*, c.name AS customer_name, c.contact_person, c.phone AS customer_phone
               FROM invoices i LEFT JOIN customers c ON c.id = i.customer_id WHERE i.id = ?', [$invoiceId]) : null;
        $this->view('penagihan/form', array_merge($this->formData(), [
            'judul' => 'Catat Aktivitas Penagihan', 'fu' => null, 'inv' => $inv,
        ]));
    }

    public function edit(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $fu = Database::i()->one(
            'SELECT f.*, i.invoice_no, c.name AS customer_name FROM collection_followups f
              LEFT JOIN invoices i ON i.id = f.invoice_id LEFT JOIN customers c ON c.id = f.customer_id
             WHERE f.id = :id AND f.deleted_at IS NULL', ['id' => $id]);
        if (!$fu) Response::abort(404, 'Data follow-up tidak ditemukan.');
        $inv = $fu['invoice_id'] ? Database::i()->one(
            'SELECT i.*, c.name AS customer_name FROM invoices i LEFT JOIN customers c ON c.id=i.customer_id WHERE i.id=?',
            [(int)$fu['invoice_id']]) : null;
        $this->view('penagihan/form', array_merge($this->formData(), [
            'judul' => 'Ubah Aktivitas Penagihan', 'fu' => $fu, 'inv' => $inv,
        ]));
    }

    private function formData(): array
    {
        $db = Database::i();
        return [
            'statuses' => $db->all('SELECT * FROM collection_statuses WHERE deleted_at IS NULL AND is_active=1 ORDER BY sort_order'),
            'officers' => $db->all('SELECT id,name FROM collection_officers WHERE deleted_at IS NULL AND is_active=1 ORDER BY name'),
            'methods' => CollectionService::METHODS,
            'priorities' => CollectionService::PRIORITIES,
        ];
    }

    public function store(): void
    {
        $this->validate(['followup_date' => 'required|date'], ['followup_date' => 'Tanggal follow-up'], '/penagihan/tambah');
        $data = [
            'invoice_id' => Request::int('invoice_id') ?: null,
            'customer_id' => Request::int('customer_id') ?: null,
            'collection_officer_id' => Request::int('collection_officer_id') ?: null,
            'followup_date' => Request::date('followup_date'),
            'method' => (string)Request::post('method', 'TELEPON'),
            'contact_person' => mb_substr((string)Request::post('contact_person', ''), 0, 120) ?: null,
            'contact_phone' => mb_substr((string)Request::post('contact_phone', ''), 0, 60) ?: null,
            'result' => mb_substr((string)Request::post('result', ''), 0, 2000) ?: null,
            'collection_status_id' => Request::int('collection_status_id') ?: null,
            'priority' => (string)Request::post('priority', 'NORMAL'),
            'next_followup_date' => Request::date('next_followup_date'),
            'notes' => mb_substr((string)Request::post('notes', ''), 0, 2000) ?: null,
        ];
        $promise = null;
        if (Request::date('promise_date') && Request::money('promise_amount') > 0) {
            $promise = [
                'promise_date' => Request::date('promise_date'),
                'promise_amount' => Request::money('promise_amount'),
                'notes' => mb_substr((string)Request::post('promise_notes', ''), 0, 500) ?: null,
            ];
        }
        try {
            $res = CollectionService::saveFollowup($data, $promise);
            Flash::success($res['pesan'] . ($res['promise_id'] ? ' Janji bayar tercatat.' : ''));
            $this->redirect($data['invoice_id'] ? '/invoice/' . $data['invoice_id'] : '/penagihan/riwayat');
        } catch (\Throwable $e) {
            Flash::error($e->getMessage());
            $this->back('/penagihan/tambah');
        }
    }

    public function update(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $db = Database::i();
        $before = $db->one('SELECT * FROM collection_followups WHERE id = :id', ['id' => $id]);
        if (!$before) Response::abort(404, 'Data follow-up tidak ditemukan.');
        $data = [
            'followup_date' => Request::date('followup_date') ?: $before['followup_date'],
            'method' => (string)Request::post('method', $before['method']),
            'collection_officer_id' => Request::int('collection_officer_id') ?: null,
            'contact_person' => mb_substr((string)Request::post('contact_person', ''), 0, 120) ?: null,
            'contact_phone' => mb_substr((string)Request::post('contact_phone', ''), 0, 60) ?: null,
            'result' => mb_substr((string)Request::post('result', ''), 0, 2000) ?: null,
            'collection_status_id' => Request::int('collection_status_id') ?: null,
            'priority' => (string)Request::post('priority', 'NORMAL'),
            'next_followup_date' => Request::date('next_followup_date'),
            'notes' => mb_substr((string)Request::post('notes', ''), 0, 2000) ?: null,
        ];
        $db->update('collection_followups', $data, 'id = :pk', ['pk' => $id]);
        Audit::logDiff('penagihan', 'update', 'collection_followup', $id, $before, $data, 'Follow-up penagihan diubah');
        Flash::success('Aktivitas penagihan berhasil diperbarui.');
        $this->redirect('/penagihan/riwayat');
    }

    public function destroy(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        Database::i()->exec('UPDATE collection_followups SET deleted_at = NOW() WHERE id = ?', [$id]);
        Audit::log('penagihan', 'delete', 'collection_followup', $id, 'Follow-up penagihan dihapus');
        Flash::success('Aktivitas penagihan dihapus.');
        $this->back('/penagihan/riwayat');
    }

    public function evaluate(): void
    {
        $stat = CollectionService::evaluatePromises();
        Audit::log('penagihan', 'update', 'promise_to_pay', null, 'Evaluasi janji bayar dijalankan', null, $stat);
        Flash::success('Evaluasi janji bayar selesai: ' . ($stat['dipenuhi'] ?? 0) . ' dipenuhi, '
            . ($stat['sebagian'] ?? 0) . ' sebagian, ' . ($stat['gagal'] ?? 0) . ' gagal.');
        $this->back('/penagihan/janji-bayar');
    }

    public function assign(): void
    {
        $ids = array_map('intval', Request::array('ids'));
        $co = Request::int('collection_officer_id');
        if (!$ids || $co <= 0) { Flash::warning('Pilih invoice dan petugas penagihan terlebih dahulu.'); $this->back('/penagihan'); }
        $n = 0;
        foreach ($ids as $id) { Database::i()->exec('UPDATE invoices SET collection_officer_id = ? WHERE id = ?', [$co, $id]); $n++; }
        Audit::log('penagihan', 'update', 'invoice', null, "$n invoice ditugaskan ke petugas penagihan #$co");
        Flash::success("$n invoice berhasil ditugaskan.");
        $this->back('/penagihan');
    }

    public function export(): void
    {
        $asOf = Request::date('tanggal_lapor') ?: Dates::today();
        $kategori = (string)Request::get('kategori', 'terlambat');
        $filters = PiutangController::readFilters(false);
        $p = CollectionService::worklist($kategori, $filters, $asOf, 'outstanding_amount', 'desc', 1,
                                         Settings::int('laporan.maks_baris_export', 50000));
        $meta = CollectionService::worklistCategories()[$kategori] ?? ['label' => 'Worklist'];

        ExportService::send((string)Request::get('format', 'xlsx'), [
            'judul' => 'WORKLIST PENAGIHAN — ' . mb_strtoupper($meta['label']),
            'tanggal_lapor' => Dates::fmt($asOf),
            'filter' => PiutangController::filterLabels($filters),
            'nama_berkas' => 'worklist_' . $kategori, 'sheet' => 'Worklist', 'orientasi' => 'landscape',
        ], [
            ['label' => 'No. Invoice',      'key' => 'invoice_no',      'type' => 'text'],
            ['label' => 'Tgl Invoice',      'key' => 'invoice_date',    'type' => 'date'],
            ['label' => 'Jatuh Tempo',      'key' => 'due_date',        'type' => 'date'],
            ['label' => 'Hari Lewat',       'key' => 'hari_lewat',      'type' => 'int'],
            ['label' => 'Customer',         'key' => 'customer_name',   'type' => 'text'],
            ['label' => 'Outlet',           'key' => 'outlet_name',     'type' => 'text'],
            ['label' => 'Narahubung',       'key' => 'contact_person',  'type' => 'text'],
            ['label' => 'Telepon',          'key' => 'customer_phone',  'type' => 'text'],
            ['label' => 'Area',             'key' => 'area_name',       'type' => 'text'],
            ['label' => 'Petugas Penagihan','key' => 'officer_name',    'type' => 'text'],
            ['label' => 'Total Tagihan',    'key' => 'net_amount',      'type' => 'money'],
            ['label' => 'Saldo',            'key' => 'outstanding_amount','type' => 'money'],
            ['label' => 'Status Penagihan', 'key' => 'status_penagihan','type' => 'text'],
            ['label' => 'Follow-up Terakhir','key' => 'followup_terakhir','type' => 'date'],
            ['label' => 'Janji Bayar',      'key' => 'janji_bayar',     'type' => 'date'],
            ['label' => 'Nilai Janji',      'key' => 'janji_nilai',     'type' => 'money'],
        ], $p['data'], [
            'invoice_no' => 'TOTAL (' . nf($p['total']) . ' invoice)',
            'outstanding_amount' => array_sum(array_map(fn($r) => (float)$r['outstanding_amount'], $p['data'])),
        ]);
    }
}
