<?php
namespace App\Services;

use App\Core\Dates;
use App\Core\Num;
use App\Core\Settings;

/**
 * Penulis CSV yang aman untuk Excel Indonesia.
 *
 * - BOM UTF-8 agar karakter non-ASCII tampil benar
 * - pemisah dapat dikonfigurasi (default titik koma, sesuai locale Indonesia)
 * - kolom teks yang berisiko dikonversi (nomor faktur pajak, nomor invoice)
 *   dibungkus sebagai ="...." sehingga nol di depan & panjang digit tetap utuh
 * - pencegahan CSV/formula injection (nilai berawalan = + - @ diberi prefiks ')
 */
class CsvWriter
{
    public static function build(array $columns, array $rows, array $opts = []): string
    {
        $sep = (string)($opts['separator'] ?? ';');
        $out = "\xEF\xBB\xBF";  // BOM

        foreach ((array)($opts['prefix_lines'] ?? []) as $line) {
            $out .= self::cell((string)$line, 'text', $sep, false) . "\n";
        }
        if (!empty($opts['prefix_lines'])) $out .= "\n";

        // Header
        $out .= implode($sep, array_map(fn($c) => self::quote((string)($c['label'] ?? ''), $sep), $columns)) . "\r\n";

        // Data
        foreach ($rows as $r) {
            $line = [];
            foreach ($columns as $i => $c) {
                $key = $c['key'] ?? $i;
                $val = is_array($r) ? ($r[$key] ?? null) : null;
                $line[] = self::cell($val, (string)($c['type'] ?? 'text'), $sep, (bool)($opts['protect_text'] ?? true));
            }
            $out .= implode($sep, $line) . "\r\n";
        }

        // Total
        if (!empty($opts['total']) && is_array($opts['total'])) {
            $line = [];
            foreach ($columns as $i => $c) {
                $key = $c['key'] ?? $i;
                $val = array_key_exists($key, $opts['total']) ? $opts['total'][$key] : ($i === 0 ? 'TOTAL' : '');
                $line[] = self::cell($val, (string)($c['type'] ?? 'text'), $sep, false);
            }
            $out .= implode($sep, $line) . "\r\n";
        }
        return $out;
    }

    private static function cell($v, string $type, string $sep, bool $protectText): string
    {
        if ($v === null || $v === '') return '';
        switch ($type) {
            case 'money':
            case 'dec':
                // Angka mentah dengan koma desimal Indonesia
                return self::quote(number_format((float)$v, $type === 'dec' ? 2 : 0, ',', ''), $sep);
            case 'int':
                return (string)(int)$v;
            case 'pct':
                return self::quote(number_format((float)$v, 2, ',', ''), $sep);
            case 'date':
                return self::quote(Dates::fmt(substr((string)$v, 0, 10)), $sep);
            default:
                $s = (string)$v;
                // Nomor panjang yang seluruhnya digit -> lindungi dari notasi ilmiah / hilangnya nol depan
                if ($protectText && preg_match('/^0\d{5,}$/', $s) || ($protectText && preg_match('/^\d{12,}$/', $s))) {
                    return '="' . str_replace('"', '""', $s) . '"';
                }
                return self::quote($s, $sep);
        }
    }

    private static function quote(string $s, string $sep): string
    {
        // Cegah formula injection
        if ($s !== '' && in_array($s[0], ['=', '+', '-', '@', "\t", "\r"], true) && !is_numeric($s)) {
            $s = "'" . $s;
        }
        if (str_contains($s, $sep) || str_contains($s, '"') || str_contains($s, "\n") || str_contains($s, "\r")) {
            return '"' . str_replace('"', '""', $s) . '"';
        }
        return $s;
    }

    public static function save(string $path, array $columns, array $rows, array $opts = []): string
    {
        $dir = dirname($path);
        if (!is_dir($dir)) @mkdir($dir, 0775, true);
        file_put_contents($path, self::build($columns, $rows, $opts));
        return $path;
    }
}
