import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:image_picker/image_picker.dart'; import '../../core/app_colors.dart'; import '../shared/reporte_chat_screen.dart'; import '../../database/db_helper.dart'; import '../../models/models.dart'; import '../../services/auth_service.dart'; class CitizenReporteScreen extends StatefulWidget { const CitizenReporteScreen({super.key}); @override State createState() => _CitizenReporteScreenState(); } class _CitizenReporteScreenState extends State { String _tipo = 'CAMION_NO_PASO'; final _desc = TextEditingController(); int _calif = 5; bool _loading = false, _sent = false; List _reportes = []; File? _foto; final _picker = ImagePicker(); static const _tipos = { 'CAMION_NO_PASO': 'El camion no paso', 'RETRASO': 'Retraso significativo', 'RESIDUOS_NO_RECOGIDOS': 'Residuos no recogidos', 'OTRO': 'Otro motivo', }; @override void initState() { super.initState(); _load(); } Future _load() async { final auth = context.read(); if (auth.currentUser == null) return; final r = await DbHelper.getReportesByUser(auth.currentUser!.id!); if (mounted) setState(() => _reportes = r); } Future _pickImage(ImageSource source) async { try { final picked = await _picker.pickImage(source: source, imageQuality: 70, maxWidth: 1024); if (picked != null && mounted) setState(() => _foto = File(picked.path)); } catch (e) { if (mounted) ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('No se pudo acceder a la camara: $e'), backgroundColor: AppColors.rojoError)); } } void _showPhotoOptions() { showModalBottomSheet(context: context, builder: (_) => SafeArea( child: Column(mainAxisSize: MainAxisSize.min, children: [ ListTile(leading: const Icon(Icons.camera_alt, color: AppColors.guindaPrimary), title: const Text('Tomar foto'), onTap: () { Navigator.pop(context); _pickImage(ImageSource.camera); }), ListTile(leading: const Icon(Icons.photo_library, color: AppColors.guindaPrimary), title: const Text('Elegir de galeria'), onTap: () { Navigator.pop(context); _pickImage(ImageSource.gallery); }), if (_foto != null) ListTile(leading: const Icon(Icons.delete_outline, color: AppColors.rojoError), title: const Text('Quitar foto', style: TextStyle(color: AppColors.rojoError)), onTap: () { Navigator.pop(context); setState(() => _foto = null); }), ]))); } Future _send() async { final auth = context.read(); if (auth.currentUser == null) return; if (_desc.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Describe el problema para poder enviar el reporte'), backgroundColor: AppColors.rojoError)); return; } setState(() => _loading = true); try { // Insertar reporte directo en la BD final db = await DbHelper.database; final id = await db.insert('reportes', { 'user_id': auth.currentUser!.id, 'tipo': _tipo, 'descripcion': _desc.text.trim(), 'colonia': auth.primaryDomicilio?.colonia ?? 'Sin colonia', 'route_id': auth.primaryDomicilio?.routeId ?? '', 'fecha': DateTime.now().toIso8601String(), 'estado': 'PENDIENTE', 'calificacion': _calif, 'foto_path': _foto?.path, }); if (id <= 0) throw Exception('No se pudo guardar el reporte'); await _load(); _desc.clear(); setState(() { _foto = null; _loading = false; _sent = true; }); await Future.delayed(const Duration(seconds: 3)); if (mounted) setState(() => _sent = false); } catch (e) { if (!mounted) return; setState(() => _loading = false); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('Error al enviar: $e'), backgroundColor: AppColors.rojoError)); } } @override Widget build(BuildContext context) => Scaffold( backgroundColor: AppColors.grisFondo, appBar: AppBar(automaticallyImplyLeading: false, backgroundColor: AppColors.guindaPrimary, foregroundColor: Colors.white, title: const Text('Reportar Incidencia'), bottom: PreferredSize(preferredSize: const Size.fromHeight(4), child: Container(height: 4, color: AppColors.dorado))), body: _sent ? Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.check_circle, color: AppColors.verdeExito, size: 72), const SizedBox(height: 16), const Text('Reporte enviado', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: AppColors.verdeExito)), const SizedBox(height: 8), const Text('El Ayuntamiento revisara tu reporte pronto.', textAlign: TextAlign.center, style: TextStyle(color: AppColors.grisTexto)), const SizedBox(height: 8), const Text('Podras chatear con ellos desde "Mis Reportes".', textAlign: TextAlign.center, style: TextStyle(color: AppColors.grisTexto, fontSize: 12)), ])) : SingleChildScrollView(padding: const EdgeInsets.all(16), child: Column(children: [ Card(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding(padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Tipo de incidencia', style: TextStyle( fontWeight: FontWeight.bold, color: AppColors.guindaPrimary, fontSize: 15)), const SizedBox(height: 8), ..._tipos.entries.map((e) => RadioListTile(dense: true, value: e.key, groupValue: _tipo, title: Text(e.value, style: const TextStyle(fontSize: 13)), activeColor: AppColors.guindaPrimary, onChanged: (v) => setState(() => _tipo = v!))), const SizedBox(height: 8), DropdownButtonFormField(value: _calif, decoration: const InputDecoration(labelText: 'Calificacion del servicio', border: OutlineInputBorder()), items: [5,4,3,2,1].map((n) => DropdownMenuItem(value: n, child: Text(['Excelente','Bueno','Regular','Malo','Muy malo'][5-n]))).toList(), onChanged: (v) => setState(() => _calif = v!)), const SizedBox(height: 10), TextField(controller: _desc, maxLines: 3, decoration: const InputDecoration(hintText: 'Describe el problema...', border: OutlineInputBorder(), filled: true, fillColor: Colors.white)), const SizedBox(height: 12), // Foto adjunta const Text('Foto del incidente (opcional)', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13)), const SizedBox(height: 8), GestureDetector( onTap: _showPhotoOptions, child: Container( width: double.infinity, height: _foto != null ? 180 : 80, decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(8), border: Border.all(color: _foto != null ? AppColors.guindaPrimary : Colors.grey.shade300, style: BorderStyle.solid)), child: _foto != null ? Stack(children: [ ClipRRect(borderRadius: BorderRadius.circular(8), child: Image.file(_foto!, fit: BoxFit.cover, width: double.infinity, height: 180)), Positioned(top: 8, right: 8, child: GestureDetector(onTap: () => setState(() => _foto = null), child: Container(padding: const EdgeInsets.all(4), decoration: const BoxDecoration( color: AppColors.rojoError, shape: BoxShape.circle), child: const Icon(Icons.close, color: Colors.white, size: 16)))), ]) : Column(mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.add_a_photo_outlined, color: AppColors.grisTexto, size: 28), const SizedBox(height: 4), const Text('Agregar foto', style: TextStyle( color: AppColors.grisTexto, fontSize: 12)), ]), ), ), const SizedBox(height: 14), SizedBox(width: double.infinity, height: 48, child: ElevatedButton.icon( onPressed: _loading ? null : _send, style: ElevatedButton.styleFrom( backgroundColor: AppColors.guindaPrimary, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))), icon: _loading ? const SizedBox(width: 18, height: 18, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2)) : const Icon(Icons.send), label: const Text('ENVIAR REPORTE', style: TextStyle(fontWeight: FontWeight.bold)))), ]))), if (_reportes.isNotEmpty) ...[ const SizedBox(height: 16), const Align(alignment: Alignment.centerLeft, child: Text('Mis Reportes', style: TextStyle(fontWeight: FontWeight.bold, color: AppColors.guindaPrimary, fontSize: 15))), const SizedBox(height: 8), ..._reportes.map((r) { final isClosed = r.estado == 'COMPLETADO'; final id = r.id ?? 0; final folio = 'RPT-${id.toString().padLeft(5, "0")}'; return Card(margin: const EdgeInsets.only(bottom: 6), child: Column(children: [ ListTile(dense: true, leading: CircleAvatar(backgroundColor: AppColors.guindaPrimary, radius: 16, child: const Icon(Icons.report, color: Colors.white, size: 16)), title: Row(children: [ Expanded(child: Text(_tipos[r.tipo] ?? r.tipo, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600))), Text(folio, style: const TextStyle(fontSize: 9, color: AppColors.grisTexto)), ]), subtitle: Text(r.descripcion, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 11)), trailing: Container(padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3), decoration: BoxDecoration(color: _estadoColor(r.estado).withOpacity(0.15), borderRadius: BorderRadius.circular(10)), child: Text(r.estado.replaceAll('_',' '), style: TextStyle(fontSize: 9, color: _estadoColor(r.estado), fontWeight: FontWeight.bold)))), if (r.id != null) Padding( padding: const EdgeInsets.fromLTRB(14, 0, 14, 8), child: SizedBox(width: double.infinity, child: OutlinedButton.icon( onPressed: () => Navigator.push(context, MaterialPageRoute( builder: (_) => ReporteChatScreen( reporteId: r.id!, folio: folio, isClosed: isClosed))), style: OutlinedButton.styleFrom( foregroundColor: isClosed ? AppColors.grisTexto : AppColors.guindaPrimary, side: BorderSide(color: isClosed ? AppColors.grisTexto : AppColors.guindaPrimary), padding: const EdgeInsets.symmetric(vertical: 6), minimumSize: Size.zero), icon: Icon(isClosed ? Icons.lock : Icons.chat_bubble_outline, size: 14), label: Text(isClosed ? 'Chat cerrado' : 'Escribir al Ayuntamiento', style: const TextStyle(fontSize: 11))))), ])); }), ], ])), ); Color _estadoColor(String e) { switch (e) { case 'RESUELTO': return AppColors.verdeExito; case 'EN_REVISION': return AppColors.azulInfo; default: return AppColors.naranjaAlerta; } } @override void dispose() { _desc.dispose(); super.dispose(); } }