import 'dart:io'; import 'dart:typed_data'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:share_plus/share_plus.dart' show ShareParams, SharePlus, XFile; import '../models/auftrag.dart'; class PdfExportService { static Future buildPdf({ required Auftrag auftrag, required List fotoBytes, Uint8List? unterschriftBytes, }) async { final doc = pw.Document(); final nr = auftrag.rechnungsnummer.isEmpty ? 'Entwurf' : auftrag.rechnungsnummer; doc.addPage( pw.MultiPage( pageFormat: PdfPageFormat.a4, margin: const pw.EdgeInsets.all(40), build: (ctx) => [ pw.Header( level: 0, child: pw.Text( 'Rechnung', style: pw.TextStyle( fontSize: 22, fontWeight: pw.FontWeight.bold, ), ), ), pw.SizedBox(height: 8), pw.Text('Rechnungs-Nr.: $nr', style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, )), pw.SizedBox(height: 16), pw.Text('Leistung / Titel: ${auftrag.titel}', style: const pw.TextStyle(fontSize: 14)), pw.SizedBox(height: 8), pw.Text('Kunde: ${auftrag.kundenName}', style: const pw.TextStyle(fontSize: 14)), if (auftrag.kundenEmail.isNotEmpty) ...[ pw.SizedBox(height: 4), pw.Text('E-Mail: ${auftrag.kundenEmail}', style: const pw.TextStyle(fontSize: 11)), ], if (auftrag.betragText.isNotEmpty) ...[ pw.SizedBox(height: 12), pw.Text('Betrag (Brutto): ${auftrag.betragText} €', style: pw.TextStyle( fontSize: 14, fontWeight: pw.FontWeight.bold, )), ], pw.SizedBox(height: 16), pw.Text('Beschreibung / Positionen:', style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, )), pw.SizedBox(height: 6), pw.Text( auftrag.beschreibung.isEmpty ? '—' : auftrag.beschreibung, style: const pw.TextStyle(fontSize: 11), ), pw.SizedBox(height: 20), if (fotoBytes.any((b) => b != null && b.isNotEmpty)) ...[ pw.Text('Fotos', style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, )), pw.SizedBox(height: 8), for (final b in fotoBytes) if (b != null && b.isNotEmpty) pw.Padding( padding: const pw.EdgeInsets.only(bottom: 12), child: pw.Image( pw.MemoryImage(b), fit: pw.BoxFit.contain, height: 200, ), ), ], if (unterschriftBytes != null && unterschriftBytes.isNotEmpty) ...[ pw.SizedBox(height: 16), pw.Text('Unterschrift Kunde', style: pw.TextStyle( fontSize: 12, fontWeight: pw.FontWeight.bold, )), pw.SizedBox(height: 8), pw.Image( pw.MemoryImage(unterschriftBytes), height: 100, fit: pw.BoxFit.contain, ), ], ], ), ); final bytes = await doc.save(); final dir = await getTemporaryDirectory(); final safeNr = nr.replaceAll(RegExp(r'[^\w\-]'), '_'); final name = 'rechnung_${safeNr.isEmpty ? auftrag.id.substring(0, 8) : safeNr}.pdf'; final file = File('${dir.path}/$name'); await file.writeAsBytes(bytes); return file; } static Future ladeUrl(String url) async { try { final r = await http.get(Uri.parse(url)); if (r.statusCode == 200) return r.bodyBytes; } catch (_) {} return null; } static Future teilen(File file, {String? rechnungsnummer}) async { await SharePlus.instance.share( ShareParams( files: [XFile(file.path)], subject: rechnungsnummer != null && rechnungsnummer.isNotEmpty ? 'Rechnung $rechnungsnummer' : 'Rechnung PDF', ), ); } }