<?php
namespace App;

use PhpOffice\PhpSpreadsheet\IOFactory;

class Render {
    /**
     * Render the final bank report XLS/XLSX into a PNG preview WITHOUT chrome.
     * Designed for shared hosting (cPanel).
     */
    public static function finalXlsToPng(string $xlsPath, string $outPngPath, array $opts = []): array {
        if (!extension_loaded('gd')) {
            return ['ok' => false, 'error' => 'gd_not_available', 'hint' => 'Enable PHP GD extension'];
        }
        if (!is_file($xlsPath)) {
            return ['ok' => false, 'error' => 'xlsx_not_found'];
        }

        $maxRows = (int)($opts['max_rows'] ?? (defined('FINAL_PHOTO_MAX_ROWS') ? FINAL_PHOTO_MAX_ROWS : 40));
        $offset = (int)($opts['offset'] ?? 0);
        $wmText  = (string)($opts['watermark_text'] ?? (defined('FINAL_PHOTO_WATERMARK_TEXT') ? FINAL_PHOTO_WATERMARK_TEXT : ''));
        $wmOpacity = (float)($opts['watermark_opacity'] ?? (defined('FINAL_PHOTO_WATERMARK_OPACITY') ? FINAL_PHOTO_WATERMARK_OPACITY : 0.12));
        $wmAngle = (int)($opts['watermark_angle'] ?? (defined('FINAL_PHOTO_WATERMARK_ANGLE') ? FINAL_PHOTO_WATERMARK_ANGLE : -25));
        $wmMode = (string)($opts['watermark_mode'] ?? (defined('FINAL_PHOTO_WATERMARK_MODE') ? FINAL_PHOTO_WATERMARK_MODE : 'tile'));
        $wmSizeOpt = (int)($opts['watermark_size'] ?? (defined('FINAL_PHOTO_WATERMARK_SIZE') ? FINAL_PHOTO_WATERMARK_SIZE : 0));
        $wmStepX = (int)($opts['watermark_step_x'] ?? (defined('FINAL_PHOTO_WATERMARK_STEP_X') ? FINAL_PHOTO_WATERMARK_STEP_X : 320));
        $wmStepY = (int)($opts['watermark_step_y'] ?? (defined('FINAL_PHOTO_WATERMARK_STEP_Y') ? FINAL_PHOTO_WATERMARK_STEP_Y : 220));
        $wmInset = (int)($opts['watermark_inset'] ?? (defined('FINAL_PHOTO_WATERMARK_INSET') ? FINAL_PHOTO_WATERMARK_INSET : 0));
        $width = (int)($opts['width'] ?? (defined('FINAL_PHOTO_WIDTH') ? FINAL_PHOTO_WIDTH : 1700));

        $font = __DIR__ . '/../assets/fonts/DejaVuSans.ttf';
        if (!is_file($font)) {
            return ['ok' => false, 'error' => 'font_missing'];
        }

        // Read sheet data
        $spread = IOFactory::load($xlsPath);
        $sheet = $spread->getActiveSheet();

        // قابل تنظیم از opts — پیش‌فرض: بانک ملی (header=6, data=7, cols C..I)
        $headerRow = (int)($opts['header_row'] ?? 6);
        $startRow  = (int)($opts['data_start'] ?? 7);
        $cols      = $opts['cols'] ?? ['C','D','E','F','G','H','I'];

        $headers = [];
        foreach ($cols as $c) {
            $headers[] = trim((string)$sheet->getCell($c.$headerRow)->getValue());
        }

        
$rows = [];
$highest = $sheet->getHighestRow();
$seen = 0;
for ($r = $startRow; $r <= $highest; $r++) {
    $row = [];
    $any = false;
    foreach ($cols as $c) {
        $v = $sheet->getCell($c.$r)->getCalculatedValue();
        $v = is_null($v) ? '' : (string)$v;
        $v = trim($v);
        if ($v !== '') $any = true;
        $row[] = $v;
    }
    if (!$any) continue;

    // offset is counted on non-empty rows (so page breaks stay stable)
    if ($seen < $offset) { $seen++; continue; }
    $seen++;

    $rows[] = $row;
    if (count($rows) >= $maxRows) break;
}

        if (empty($rows)) {
            return ['ok' => false, 'error' => 'no_rows', 'offset' => $offset, 'rendered_rows' => 0];
        }

        // Layout
        $pad = 18;
        $top = 95;      // top area
        $headerH = 54;
        $rowH = 42;
        $innerW = max(800, $width - ($pad * 2));

        // Column width weights — قابل override از opts
        $weights = $opts['col_weights'] ?? [
            170, // C: مبلغ (ملی)
            160, // D: شناسه واریز
            140, // E: نوع انتقال
            310, // F: بابت
            170, // G: کدپیگیری
            110, // H: وضعیت
            190, // I: شرح تراکنش
        ];
        $sum = array_sum($weights);
        $colW = [];
        foreach ($weights as $w) {
            $colW[] = (int)round(($w / $sum) * $innerW);
        }
        $diff = $innerW - array_sum($colW);
        $colW[count($colW) - 1] += $diff;

        $height = $top + $headerH + (count($rows) * $rowH) + $pad;
        if ($height < 350) $height = 350;

        $im = imagecreatetruecolor($width, $height);
        if (!$im) {
            return ['ok' => false, 'error' => 'gd_create_failed'];
        }

        // Enable alpha
        imagealphablending($im, true);
        imagesavealpha($im, true);

        // Colors
        $cBg = imagecolorallocate($im, 242, 242, 242);
        $cWhite = imagecolorallocate($im, 255, 255, 255);
        $cHeader = imagecolorallocate($im, 43, 43, 43);
        $cHeaderText = imagecolorallocate($im, 255, 255, 255);
        $cRow1 = imagecolorallocate($im, 217, 217, 217);
        $cRow2 = imagecolorallocate($im, 209, 209, 209);
        $cGrid = imagecolorallocate($im, 189, 189, 189);
        $cText = imagecolorallocate($im, 0, 0, 0);

        // Background + card
        imagefilledrectangle($im, 0, 0, $width, $height, $cBg);
        imagefilledrectangle($im, $pad, $pad, $width - $pad, $height - $pad, $cWhite);

        // Table origin
        $tableX = $pad;
        $tableY = $top;
        $tableW = $innerW;

        // Header row
        $xRight = $tableX + $tableW;
        $y = $tableY;
        for ($i = 0; $i < count($headers); $i++) {
            $wcol = $colW[$i];
            $x1 = $xRight - $wcol;
            imagefilledrectangle($im, $x1, $y, $xRight, $y + $headerH, $cHeader);
            imagerectangle($im, $x1, $y, $xRight, $y + $headerH, $cHeader);

            $txt = trim((string)($headers[$i] ?? ''));
            if ($txt === '') $txt = $cols[$i];
            $txt = RTL::fix($txt);
            self::drawCenteredText($im, $txt, $font, 14, $x1, $y, $wcol, $headerH, $cHeaderText);

            $xRight = $x1;
        }

        // Body rows
        $y = $tableY + $headerH;
        for ($r = 0; $r < count($rows); $r++) {
            $row = $rows[$r];
            $bg = ($r % 2 === 0) ? $cRow1 : $cRow2;

            $xRight = $tableX + $tableW;
            for ($i = 0; $i < count($cols); $i++) {
                $wcol = $colW[$i];
                $x1 = $xRight - $wcol;

                imagefilledrectangle($im, $x1, $y, $xRight, $y + $rowH, $bg);
                imagerectangle($im, $x1, $y, $xRight, $y + $rowH, $cGrid);

                $cell = trim((string)($row[$i] ?? ''));
                if ($cell !== '') {
                    $cell = RTL::fix($cell);
                    self::drawCenteredText($im, $cell, $font, 13, $x1, $y, $wcol, $rowH, $cText);
                }

                $xRight = $x1;
            }

            $y += $rowH;
        }

        // Watermark (big and faint) - draw AFTER the table so it stays visible.
        // On some shared-hosting GD builds, alpha in imagettftext can be inconsistent.
        // So we draw the watermark on a transparent overlay and then alpha-blend it onto the main image.
        if ($wmText !== '') {
            $opacity = max(0.0, min(1.0, $wmOpacity));
            // Size: allow explicit override; otherwise choose a modest default.
            $wmSize = $wmSizeOpt > 0 ? $wmSizeOpt : (int)min(90, max(48, $width / 26));
            $wmDraw = RTL::fix($wmText);
            $mode = strtolower(trim($wmMode));
            if ($mode !== 'single' && $mode !== 'tile') {
                $mode = 'tile';
            }

            $ov = imagecreatetruecolor($width, $height);
            if ($ov) {
                imagealphablending($ov, false);
                imagesavealpha($ov, true);
                $transparent = imagecolorallocatealpha($ov, 0, 0, 0, 127);
                imagefilledrectangle($ov, 0, 0, $width, $height, $transparent);
                imagealphablending($ov, true);

                // Convert opacity (0..1) to GD alpha (0..127). Lower alpha = more visible.
                $alpha = (int)round(127 * (1 - $opacity));
                $cWm = imagecolorallocatealpha($ov, 20, 20, 20, $alpha);

                $bbox = imagettfbbox($wmSize, $wmAngle, $font, $wmDraw);
                if ($bbox) {
                    $xs = [$bbox[0], $bbox[2], $bbox[4], $bbox[6]];
                    $ys = [$bbox[1], $bbox[3], $bbox[5], $bbox[7]];
                    $minX = min($xs);
                    $maxX = max($xs);
                    $minY = min($ys);
                    $maxY = max($ys);
                    $tw = $maxX - $minX;
                    $th = $maxY - $minY;

                    if ($mode === 'single') {
                        $cx = (int)($width * 0.50);
                        $cy = (int)($height * 0.55);
                        $x = (int)round($cx - ($tw / 2) - $minX);
                        $y = (int)round($cy + ($th / 2) - $minY);
                        @imagettftext($ov, $wmSize, $wmAngle, $x, $y, $cWm, $font, $wmDraw);
                    } else {
                        // Tiled watermark (small, repeated across the image)
                        $stepX = max(140, $wmStepX);
                        $stepY = max(120, $wmStepY);
                        $inset = max(0, $wmInset);

                        // Start a bit off-canvas so the tile covers the edges even when rotated.
                        $startX = -$stepX;
                        $startY = -$stepY;
                        $endX = $width + $stepX;
                        $endY = $height + $stepY;

                        $row = 0;
                        for ($yy = $startY; $yy <= $endY; $yy += $stepY) {
                            $row++;
                            $xOffset = ($row % 2 === 0) ? (int)($stepX / 2) : 0;
                            for ($xx = $startX; $xx <= $endX; $xx += $stepX) {
                                $cx = $xx + $xOffset + $inset;
                                $cy = $yy + $inset;
                                $x = (int)round($cx - ($tw / 2) - $minX);
                                $y = (int)round($cy + ($th / 2) - $minY);
                                @imagettftext($ov, $wmSize, $wmAngle, $x, $y, $cWm, $font, $wmDraw);
                            }
                        }
                    }
                }

                // Blend overlay onto main image
                imagecopy($im, $ov, 0, 0, 0, 0, $width, $height);
                imagedestroy($ov);
            }
        }

        // Output
        $outDir = dirname($outPngPath);
        if (!is_dir($outDir)) {
            @mkdir($outDir, 0775, true);
        }

        $ok = @imagepng($im, $outPngPath, 6);
        imagedestroy($im);

        if (!$ok || !is_file($outPngPath) || filesize($outPngPath) < 1000) {
            return ['ok' => false, 'error' => 'png_write_failed'];
        }

        return ['ok' => true, 'method' => 'gd', 'png' => $outPngPath, 'offset' => $offset, 'rendered_rows' => count($rows)];
    }

    private static function drawCenteredText($im, string $text, string $font, int $size, int $x, int $y, int $w, int $h, int $color): void {
        $text = self::truncateToFit($text, $font, $size, max(10, $w - 10));
        $bbox = imagettfbbox($size, 0, $font, $text);
        if (!$bbox) return;
        $tw = abs($bbox[4] - $bbox[0]);
        $th = abs($bbox[5] - $bbox[1]);

        $tx = $x + (int)(($w - $tw) / 2);
        $ty = $y + (int)(($h + $th) / 2) - 2;

        @imagettftext($im, $size, 0, $tx, $ty, $color, $font, $text);
    }

    private static function truncateToFit(string $text, string $font, int $size, int $maxWidth): string {
        $text = (string)$text;
        if ($text === '') return '';

        $bbox = imagettfbbox($size, 0, $font, $text);
        if ($bbox) {
            $tw = abs($bbox[4] - $bbox[0]);
            if ($tw <= $maxWidth) return $text;
        }

        $chars = preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY) ?: [];
        $lo = 0;
        $hi = count($chars);
        $best = '';
        while ($lo <= $hi) {
            $mid = (int)(($lo + $hi) / 2);
            $cand = implode('', array_slice($chars, 0, $mid)) . '…';
            $bb = imagettfbbox($size, 0, $font, $cand);
            $w = $bb ? abs($bb[4] - $bb[0]) : PHP_INT_MAX;
            if ($w <= $maxWidth) {
                $best = $cand;
                $lo = $mid + 1;
            } else {
                $hi = $mid - 1;
            }
        }
        return $best !== '' ? $best : '…';
    }

/**
 * Render multiple pages of the final XLS into multiple PNG files.
 * Returns: ['ok'=>true, 'pngs'=>[...], 'pages'=>N] or ['ok'=>false, 'error'=>...]
 */
public static function finalXlsToPngPages(string $xlsPath, string $outPrefix, array $opts = []): array {
    $perPage = (int)($opts['per_page'] ?? ($opts['max_rows'] ?? (defined('FINAL_PHOTO_MAX_ROWS') ? FINAL_PHOTO_MAX_ROWS : 40)));
    if ($perPage <= 0) $perPage = 40;

    // Normalize prefix: if it ends with .png, drop it
    if (preg_match('/\.png$/i', $outPrefix)) {
        $outPrefix = preg_replace('/\.png$/i', '', $outPrefix);
    }

    $pngs = [];
    $meta = [];
    $offset = 0;
    $page = 1;
    while (true) {
        $out = $outPrefix . '_p' . $page . '.png';
        $r = self::finalXlsToPng($xlsPath, $out, array_merge($opts, [
            'max_rows' => $perPage,
            'offset' => $offset,
        ]));
        if (empty($r['ok'])) {
            // stop when we have no more rows
            if (($r['error'] ?? '') === 'no_rows' && $page > 1) break;
            return $r;
        }
        $pngs[] = $out;

        $meta[] = [
            'page' => $page,
            'offset' => (int)($r['offset'] ?? $offset),
            'rows' => (int)($r['rendered_rows'] ?? $perPage),
        ];

        $offset += $perPage;
        $page++;
        // safety guard
        if ($page > 50) break;
    }

    return ['ok' => true, 'pngs' => $pngs, 'pages' => count($pngs), 'meta' => $meta];
}

}
