// tool/gen_bench_pdf.dart
//
// Generates a benchmark PDF at test/assets/large_300p.pdf using
// package:syncfusion_flutter_pdf.
//
// syncfusion_flutter_pdf imports dart:ui which is only available inside the
// Flutter SDK runtime, so this file CANNOT be run via plain `dart run`.
//
// USAGE:
//   flutter test tool/gen_bench_pdf.dart
//   flutter test tool/gen_bench_pdf.dart --dart-define=PAGE_COUNT=50
//
// The script is structured as a flutter_test file (one `test(...)` block) so
// that `flutter test` invokes it with the full Flutter engine (dart:ui present).
// It is NOT a real unit test — it is a code-generation tool that happens to
// need the Flutter runtime. The test "passes" as long as the file is written
// successfully.
//
// Each page contains:
//   - A bold title (page number heading)
//   - Two paragraphs of body text
//   - A ruled grid of lines (10x10)
//   - A filled rectangle and an outlined ellipse
// This gives a realistic (non-blank) render load for pdfrx frame-timing tests.

// ignore_for_file: avoid_print

import 'dart:io';
import 'dart:math';
import 'dart:ui' show Offset, Rect, Size;

import 'package:flutter_test/flutter_test.dart';
import 'package:syncfusion_flutter_pdf/pdf.dart';

void main() {
  // Read PAGE_COUNT from --dart-define (default 300).
  const int pageCount = int.fromEnvironment('PAGE_COUNT', defaultValue: 300);

  test('generate test/assets/large_300p.pdf ($pageCount pages)', () {
    final outputPath = _resolveOutputPath();
    final outFile = File(outputPath);
    outFile.parent.createSync(recursive: true);

    final pdf = PdfDocument();

    // Reusable fonts and brushes (created once, shared across pages).
    final titleFont = PdfStandardFont(PdfFontFamily.helvetica, 18,
        style: PdfFontStyle.bold);
    final bodyFont = PdfStandardFont(PdfFontFamily.helvetica, 10);
    final smallFont = PdfStandardFont(PdfFontFamily.helvetica, 8);

    final blackBrush = PdfSolidBrush(PdfColor(0, 0, 0));
    final darkBlueBrush = PdfSolidBrush(PdfColor(10, 30, 80));
    final lightGrayBrush = PdfSolidBrush(PdfColor(220, 220, 220));
    final accentBrush = PdfSolidBrush(PdfColor(60, 100, 200));

    final gridPen = PdfPen(PdfColor(180, 180, 180), width: 0.3);
    final borderPen = PdfPen(PdfColor(0, 0, 0), width: 1.0);
    final accentPen = PdfPen(PdfColor(60, 100, 200), width: 1.5);

    final rng = Random(42); // deterministic

    for (int i = 1; i <= pageCount; i++) {
      final page = pdf.pages.add();
      final g = page.graphics;
      final w = page.getClientSize().width;
      final h = page.getClientSize().height;

      // ── Title ──────────────────────────────────────────────────────────────
      g.drawString(
        'BadNote Benchmark — Page $i of $pageCount',
        titleFont,
        brush: darkBlueBrush,
        bounds: Rect.fromLTWH(36, 30, w - 72, 28),
      );

      // Horizontal rule under title
      g.drawLine(
        PdfPen(PdfColor(60, 100, 200), width: 1.0),
        Offset(36, 62),
        Offset(w - 36, 62),
      );

      // ── Body text (two paragraphs) ────────────────────────────────────────
      final paragraph1 =
          'This page is part of a synthetic $pageCount-page benchmark PDF '
          'generated by BadNote\'s tool/gen_bench_pdf.dart. Each page carries '
          'non-trivial content (text, vector shapes, a line grid) to simulate '
          'realistic rendering load for pdfrx frame-timing measurements. '
          'Page index: $i. Seed value: ${rng.nextInt(99999)}.';

      final paragraph2 =
          'Performance target (MUST #4, §10/M1): pdfrx fling-scroll over '
          '$pageCount pages in profile mode must stay at ≤ 16.6 ms median '
          'frame time (build + raster) and ≤ 22 ms at p95, measured over '
          'N ≥ 120 frames per §7.1 of the BadNote Phase 1 plan. If this gate '
          'fails the backend choice is invalidated. Fill: ${_lorem(rng, 60)}.';

      g.drawString(
        paragraph1,
        bodyFont,
        brush: blackBrush,
        bounds: Rect.fromLTWH(36, 72, w - 72, 80),
        format: PdfStringFormat(lineSpacing: 4),
      );

      g.drawString(
        paragraph2,
        bodyFont,
        brush: blackBrush,
        bounds: Rect.fromLTWH(36, 158, w - 72, 80),
        format: PdfStringFormat(lineSpacing: 4),
      );

      // ── 10×10 ruled grid ──────────────────────────────────────────────────
      const gridLeft = 36.0;
      const gridTop = 260.0;
      final gridWidth = w - 72;
      const gridHeight = 220.0;
      const cols = 10;
      const rows = 10;
      final cellW = gridWidth / cols;
      const cellH = gridHeight / rows;

      for (int col = 0; col <= cols; col++) {
        final x = gridLeft + col * cellW;
        g.drawLine(
            gridPen, Offset(x, gridTop), Offset(x, gridTop + gridHeight));
      }
      for (int row = 0; row <= rows; row++) {
        const y = gridTop;
        g.drawLine(gridPen, Offset(gridLeft, y + row * cellH),
            Offset(gridLeft + gridWidth, y + row * cellH));
      }

      for (int row = 0; row < rows; row++) {
        for (int col = 0; col < cols; col++) {
          if ((row + col) % 3 == 0) {
            g.drawRectangle(
              brush: lightGrayBrush,
              bounds: Rect.fromLTWH(
                gridLeft + col * cellW + 0.5,
                gridTop + row * cellH + 0.5,
                cellW - 1,
                cellH - 1,
              ),
            );
          }
        }
      }

      g.drawRectangle(
        pen: borderPen,
        bounds: Rect.fromLTWH(gridLeft, gridTop, gridWidth, gridHeight),
      );

      for (int row = 0; row < rows; row++) {
        g.drawString(
          'R${row + 1}',
          smallFont,
          brush: blackBrush,
          bounds: Rect.fromLTWH(
            gridLeft + 2,
            gridTop + row * cellH + 2,
            cellW - 4,
            cellH - 4,
          ),
        );
      }

      // ── Accent shapes ──────────────────────────────────────────────────────
      const shapeTop = gridTop + gridHeight + 18;
      final rectW = 60.0 + (i % 8) * 10.0;

      g.drawRectangle(
        pen: accentPen,
        brush: accentBrush,
        bounds: Rect.fromLTWH(36, shapeTop, rectW, 24),
      );
      g.drawString(
        'Page $i',
        smallFont,
        brush: PdfSolidBrush(PdfColor(255, 255, 255)),
        bounds: Rect.fromLTWH(40, shapeTop + 6, rectW - 8, 14),
      );

      g.drawEllipse(
        Rect.fromLTWH(36 + rectW + 16, shapeTop, 80, 24),
        pen: accentPen,
      );

      // ── Footer ────────────────────────────────────────────────────────────
      g.drawString(
        'BadNote bench PDF • page $i/$pageCount • tool/gen_bench_pdf.dart',
        smallFont,
        brush: PdfSolidBrush(PdfColor(140, 140, 140)),
        bounds: Rect.fromLTWH(36, h - 28, w - 72, 18),
        format: PdfStringFormat(alignment: PdfTextAlignment.center),
      );
    }

    final bytes = pdf.saveSync();
    pdf.dispose();

    outFile.writeAsBytesSync(bytes);

    final sizeKb = (outFile.lengthSync() / 1024).toStringAsFixed(1);
    print('Generated: $outputPath');
    print('Pages: $pageCount');
    print('Size: ${sizeKb} KB (${outFile.lengthSync()} bytes)');

    expect(outFile.existsSync(), isTrue);
    expect(outFile.lengthSync(), greaterThan(1024),
        reason: 'PDF must be at least 1 KB');
  }, timeout: const Timeout(Duration(minutes: 5)));
}

/// Resolves test/assets/large_300p.pdf relative to this script's location.
/// tool/gen_bench_pdf.dart → project root → test/assets/large_300p.pdf
String _resolveOutputPath() {
  // When run via `flutter test`, the CWD is the project root.
  return 'test/assets/large_300p.pdf';
}

/// Generates a deterministic Lorem-Ipsum-style filler of roughly [words] words.
String _lorem(Random rng, int words) {
  const vocab = [
    'lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur',
    'adipiscing', 'elit', 'sed', 'eiusmod', 'tempor', 'incididunt',
    'labore', 'dolore', 'magna', 'aliqua', 'enim', 'minim', 'veniam',
    'quis', 'nostrud', 'exercitation', 'ullamco', 'laboris', 'nisi',
    'aliquip', 'commodo', 'consequat', 'duis', 'aute', 'irure',
    'reprehenderit', 'voluptate', 'velit', 'esse', 'cillum', 'fugiat',
    'nulla', 'pariatur', 'excepteur', 'sint', 'occaecat', 'cupidatat',
    'proident', 'culpa', 'officia', 'deserunt', 'mollit', 'anim',
  ];
  return List.generate(words, (_) => vocab[rng.nextInt(vocab.length)])
      .join(' ');
}
