<?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\Request;
use App\Core\Response;
use App\Core\Settings;
use App\Services\AgingService;
use App\Services\ExportService;
use App\Services\PdfWriter;
use App\Services\ReportBuilder;

/** Pembuat laporan fleksibel. */
class ReportBuilderController extends Controller
{
    protected string $module = 'report_builder';

    public function index(): void
    {
        $this->view('pembuat_laporan/index', [
            'judul' => 'Pembuat Laporan',
            'subjudul' => 'Susun laporan Anda sendiri: pilih sumber data, kolom, filter, pengelompokan, dan chart',
            'templates' => ReportBuilder::accessibleTemplates(),
            'sources' => ReportBuilder::dataSources(),
        ]);
    }

    public function builder(array $params = []): void
    {
        $id = (int)($params['id'] ?? 0);
        $def = null;
        if ($id > 0) {
            $def = ReportBuilder::loadTemplate($id);
            if (!$def) Response::abort(404, 'Template laporan tidak ditemukan.');
        }
        $source = $def['data_source'] ?? (string)Request::get('sumber', 'invoices');
        if (!isset(ReportBuilder::dataSources()[$source])) $source = 'invoices';

        $this->view('pembuat_laporan/builder', [
            'judul' => $def ? 'Pembuat Laporan — ' . $def['name'] : 'Pembuat Laporan Baru',
            'subjudul' => ReportBuilder::dataSources()[$source]['deskripsi'],
            'def' => $def, 'source' => $source,
            'sources' => ReportBuilder::dataSources(),
            'fields' => ReportBuilder::fields($source),
            'operators' => ReportBuilder::operators(),
            'aggregates' => ReportBuilder::aggregates(),
            'chartTypes' => ReportBuilder::chartTypes(),
            'templates' => ReportBuilder::accessibleTemplates(),
            'agingConfigs' => AgingService::allConfigs(),
        ]);
    }

    /** Daftar field sebuah sumber data (JSON, untuk pergantian sumber tanpa reload). */
    public function fields(): void
    {
        $src = (string)Request::get('sumber', 'invoices');
        if (!isset(ReportBuilder::dataSources()[$src])) $src = 'invoices';
        $f = ReportBuilder::fields($src);
        $out = [];
        foreach ($f as $k => $d) {
            $out[] = ['key' => $k, 'label' => $d['label'], 'type' => $d['type'],
                      'group' => $d['group'] ?? 'Lain', 'agg' => !empty($d['agg'])];
        }
        $this->json(['sukses' => true, 'sumber' => $src, 'fields' => $out]);
    }

    /** Membaca definisi laporan dari permintaan. */
    private function readDefinition(): array
    {
        $raw = Request::post('definisi');
        if (is_string($raw) && $raw !== '') {
            $d = json_decode($raw, true);
            if (is_array($d)) return $this->sanitize($d);
        }
        return $this->sanitize(Request::all());
    }

    private function sanitize(array $d): array
    {
        $src = (string)($d['data_source'] ?? 'invoices');
        if (!isset(ReportBuilder::dataSources()[$src])) $src = 'invoices';
        return [
            'name'        => mb_substr(trim((string)($d['name'] ?? 'Laporan Tanpa Nama')), 0, 180),
            'description' => mb_substr(trim((string)($d['description'] ?? '')), 0, 500),
            'data_source' => $src,
            'chart_type'  => isset(ReportBuilder::chartTypes()[$d['chart_type'] ?? 'none']) ? (string)$d['chart_type'] : 'none',
            'group_by'    => array_values(array_filter((array)($d['group_by'] ?? []))),
            'columns'     => array_values((array)($d['columns'] ?? [])),
            'filters'     => array_values((array)($d['filters'] ?? [])),
            'pivot'       => !empty($d['pivot']['column']) ? (array)$d['pivot'] : null,
            'options'     => (array)($d['options'] ?? []),
            'visibility'  => in_array($d['visibility'] ?? 'private', ['private', 'shared', 'public'], true) ? (string)$d['visibility'] : 'private',
            'chart_config'=> (array)($d['chart_config'] ?? []),
        ];
    }

    /** Menjalankan laporan dan mengembalikan hasil (JSON). */
    public function run(): void
    {
        $def = $this->readDefinition();
        try {
            $res = ReportBuilder::execute($def, min(5000, (int)($def['options']['limit'] ?? 1000)));
        } catch (\Throwable $e) {
            $this->json(['sukses' => false, 'pesan' => 'Laporan gagal dijalankan: ' . $e->getMessage()], 422);
        }

        // Susun tabel siap render
        $cols = [];
        foreach ($res['columns'] as $c) {
            $cols[] = ['alias' => $c['alias'], 'label' => $c['label'], 'type' => $c['type'], 'aggregate' => $c['aggregate']];
        }
        foreach ($res['pivot_columns'] as $pc) {
            $cols[] = ['alias' => $pc['alias'], 'label' => $pc['label'], 'type' => $pc['type'], 'aggregate' => 'sum'];
        }
        $rows = [];
        foreach ($res['rows'] as $r) {
            $row = [];
            foreach ($cols as $c) {
                $gt = $res['total'][$c['alias']] ?? null;
                $row[$c['alias']] = [
                    'raw' => $r[$c['alias']] ?? null,
                    'txt' => ReportBuilder::formatCell($r[$c['alias']] ?? null, $c['type'], $gt, $c['aggregate']),
                ];
            }
            $rows[] = $row;
        }
        $total = [];
        foreach ($cols as $c) {
            if (!array_key_exists($c['alias'], $res['total'])) { $total[$c['alias']] = ''; continue; }
            $total[$c['alias']] = ReportBuilder::formatCell($res['total'][$c['alias']], $c['type'], null, 'none');
        }

        // Data chart
        $chart = null;
        if (($def['chart_type'] ?? 'none') !== 'none' && $res['rows']) {
            $labelAlias = $res['columns'][0]['alias'] ?? null;
            if ($res['group_by']) $labelAlias = 'g0';
            $valueCols = array_values(array_filter($res['columns'], fn($c) => in_array($c['type'], ['money', 'int', 'dec', 'pct'], true)));
            $chart = [
                'type' => $def['chart_type'],
                'labels' => array_map(fn($r) => (string)($r[$labelAlias] ?? '-'), array_slice($res['rows'], 0, 40)),
                'datasets' => array_map(fn($c) => [
                    'label' => $c['label'],
                    'data' => array_map(fn($r) => (float)($r[$c['alias']] ?? 0), array_slice($res['rows'], 0, 40)),
                ], array_slice($valueCols, 0, 4)),
            ];
        }

        $this->json([
            'sukses' => true,
            'kolom' => $cols, 'baris' => $rows, 'total' => $total,
            'jumlah_baris' => $res['jumlah_baris'], 'ditampilkan' => count($rows),
            'terpotong' => $res['terpotong'], 'chart' => $chart,
            'subtotal' => $res['subtotals'], 'group_by' => $res['group_by'],
        ]);
    }

    public function save(): void
    {
        $def = $this->readDefinition();
        if ($def['name'] === '') { $this->json(['sukses' => false, 'pesan' => 'Nama laporan wajib diisi.'], 422); }
        $id = Request::int('template_id') ?: null;
        if ($id) {
            $t = Database::i()->one('SELECT * FROM report_templates WHERE id = ? AND deleted_at IS NULL', [$id]);
            if (!$t) $this->json(['sukses' => false, 'pesan' => 'Template tidak ditemukan.'], 404);
            if ((int)$t['is_system'] === 1 && !Auth::isSuper()) {
                $this->json(['sukses' => false, 'pesan' => 'Template bawaan sistem tidak dapat diubah. Gunakan tombol Duplikat.'], 403);
            }
            if ($t['owner_id'] && (int)$t['owner_id'] !== Auth::id() && !Auth::isSuper()) {
                $this->json(['sukses' => false, 'pesan' => 'Anda hanya dapat mengubah template milik sendiri.'], 403);
            }
        }
        $newId = ReportBuilder::saveTemplate($def, $id);
        Audit::log('report_builder', $id ? 'update' : 'create', 'report_template', $newId, 'Template laporan disimpan: ' . $def['name']);
        $this->json(['sukses' => true, 'id' => $newId, 'pesan' => 'Template laporan berhasil disimpan.',
                     'redirect' => url('/pembuat-laporan/' . $newId)]);
    }

    public function duplicate(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        try {
            $newId = ReportBuilder::duplicateTemplate($id, trim((string)Request::post('nama', '')) ?: null);
            Audit::log('report_builder', 'create', 'report_template', $newId, 'Template laporan diduplikasi dari #' . $id);
            Flash::success('Template berhasil diduplikasi.');
            $this->redirect('/pembuat-laporan/' . $newId);
        } catch (\Throwable $e) {
            Flash::error($e->getMessage());
            $this->back('/pembuat-laporan');
        }
    }

    public function share(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $db = Database::i();
        $t = $db->one('SELECT * FROM report_templates WHERE id = ? AND deleted_at IS NULL', [$id]);
        if (!$t) Response::abort(404, 'Template tidak ditemukan.');
        if ((int)$t['is_system'] !== 1 && (int)$t['owner_id'] !== Auth::id() && !Auth::isSuper()) {
            Response::abort(403, 'Anda hanya dapat membagikan template milik sendiri.');
        }
        $vis = (string)Request::post('visibility', 'private');
        if (!in_array($vis, ['private', 'shared', 'public'], true)) $vis = 'private';
        $db->update('report_templates', ['visibility' => $vis], 'id = :pk', ['pk' => $id]);

        $users = array_map('intval', Request::array('user_ids'));
        $db->exec('DELETE FROM report_template_shares WHERE report_template_id = ?', [$id]);
        foreach ($users as $uid) {
            if ($uid > 0) $db->exec('INSERT IGNORE INTO report_template_shares (report_template_id, user_id) VALUES (?,?)', [$id, $uid]);
        }
        Audit::log('report_builder', 'update', 'report_template', $id, 'Pembagian template diubah menjadi ' . $vis);
        Flash::success('Pengaturan berbagi template disimpan.');
        $this->back('/pembuat-laporan');
    }

    public function destroy(array $params): void
    {
        $id = (int)($params['id'] ?? 0);
        $db = Database::i();
        $t = $db->one('SELECT * FROM report_templates WHERE id = ? AND deleted_at IS NULL', [$id]);
        if (!$t) Response::abort(404, 'Template tidak ditemukan.');
        if ((int)$t['is_system'] === 1) { Flash::error('Template bawaan sistem tidak dapat dihapus.'); $this->back('/pembuat-laporan'); }
        if ((int)$t['owner_id'] !== Auth::id() && !Auth::isSuper()) {
            Flash::error('Anda hanya dapat menghapus template milik sendiri.');
            $this->back('/pembuat-laporan');
        }
        $db->exec('UPDATE report_templates SET deleted_at = NOW() WHERE id = ?', [$id]);
        Audit::log('report_builder', 'delete', 'report_template', $id, 'Template laporan dihapus: ' . $t['name']);
        Flash::success('Template laporan dihapus.');
        $this->redirect('/pembuat-laporan');
    }

    public function export(): void
    {
        $def = $this->readDefinition();
        $format = (string)Request::post('format', Request::get('format', 'xlsx'));
        $res = ReportBuilder::execute($def, Settings::int('laporan.maks_baris_export', 50000));

        $columns = [];
        foreach ($res['columns'] as $c) $columns[] = ['label' => $c['label'], 'key' => $c['alias'], 'type' => $c['type']];
        foreach ($res['pivot_columns'] as $pc) $columns[] = ['label' => $pc['label'], 'key' => $pc['alias'], 'type' => $pc['type']];

        $filterLabels = [];
        foreach ($def['filters'] as $f) {
            if (empty($f['field'])) continue;
            $fd = $res['fields'][$f['field']] ?? null;
            $op = ReportBuilder::operators()[$f['operator'] ?? 'eq']['label'] ?? '';
            $filterLabels[$fd['label'] ?? $f['field']] = trim($op . ' ' . (string)($f['value1'] ?? '')
                . (!empty($f['value2']) ? ' s/d ' . $f['value2'] : ''));
        }

        ExportService::send($format, [
            'judul' => mb_strtoupper($def['name'] ?: 'LAPORAN'),
            'subjudul' => ($def['description'] ?: ReportBuilder::dataSources()[$def['data_source']]['label'])
                . ($res['group_by'] ? ' · Dikelompokkan per ' . implode(', ', array_map(fn($g) => $res['fields'][$g]['label'] ?? $g, $res['group_by'])) : ''),
            'tanggal_lapor' => Dates::fmt($res['as_of']),
            'filter' => $filterLabels,
            'nama_berkas' => $def['name'] ?: 'laporan',
            'sheet' => mb_substr($def['name'] ?: 'Laporan', 0, 31),
            'orientasi' => count($columns) > 6 ? 'landscape' : 'portrait',
            'catatan' => $res['terpotong'] ? 'Catatan: hasil dibatasi ' . nf(count($res['rows'])) . ' baris dari total ' . nf($res['jumlah_baris']) . ' baris.' : null,
        ], $columns, $res['rows'], $res['total']);
    }
}
