import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'recognition_page.dart';
import 'result_page.dart';
import 'live_scan_page.dart';
import '../services/database_service.dart';
import '../services/ad_helper.dart';

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  @override
  MainPageState createState() => MainPageState();
}

class MainPageState extends State<MainPage> {
  final TextEditingController _ingredientController = TextEditingController();
  final InterstitialAdManager _interstitialAdManager = InterstitialAdManager();

  @override
  void initState() {
    super.initState();
    _interstitialAdManager.loadAd();
  }

  @override
  void dispose() {
    _ingredientController.dispose();
    _interstitialAdManager.dispose();
    super.dispose();
  }

  Future<void> _navigateToLiveScan() async {
    final result = await Navigator.push<Map<String, dynamic>>(
      context,
      MaterialPageRoute(builder: (context) => const LiveScanPage()),
    );
    if (result != null) {
      final ingredients = result['ingredients'] as List<String>;
      final genericSulfateOrSulfonateDetected =
          result['genericSulfateOrSulfonateDetected'] as bool;

      setState(() {
        _ingredientController.text = ingredients.join(', ');
      });

      try {
        final details =
            await DatabaseHelper.instance.fetchIngredientsDetails(ingredients);
        if (!mounted) return;

        await Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => ResultPage(
              genericSulfateOrSulfonateDetected:
                  genericSulfateOrSulfonateDetected,
              ingredientsDetails: details,
              adManager: _interstitialAdManager,
            ),
          ),
        );
      } catch (e) {
        _showErrorSnackBar('Error fetching ingredient details: $e');
      }
    }
  }

  Future<void> _pickImage() async {
    try {
      final pickedImage =
          await ImagePicker().pickImage(source: ImageSource.gallery);
      if (pickedImage != null) {
        final result = await Navigator.push<String>(
          context,
          MaterialPageRoute(
            builder: (context) => RecognitionPage(xFile: pickedImage),
          ),
        );
        if (result != null) {
          setState(() {
            _ingredientController.text = result;
          });
        }
      }
    } catch (e) {
      _showErrorSnackBar('Error picking image: $e');
    }
  }

  Future<void> _handleIngredients() async {
    final ingredientsText = _ingredientController.text
        .split(',')
        .map((s) => s.trim())
        .where((s) => s.isNotEmpty)
        .toList();

    if (ingredientsText.isEmpty) {
      _showErrorSnackBar('Please enter at least one ingredient.');
      return;
    }

    try {
      final details = await DatabaseHelper.instance
          .fetchIngredientsDetails(ingredientsText);
      if (!mounted) return;

      await Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => ResultPage(
            genericSulfateOrSulfonateDetected: false,
            ingredientsDetails: details,
            adManager: _interstitialAdManager,
          ),
        ),
      );
    } catch (e) {
      _showErrorSnackBar('Error fetching ingredient details: $e');
    }
  }

  void _showErrorSnackBar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        behavior: SnackBarBehavior.floating,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(
          "Curlify",
          style: theme.textTheme.headlineSmall?.copyWith(
            fontWeight: FontWeight.bold,
          ),
        ),
        elevation: 0,
        backgroundColor: Colors.transparent,
        centerTitle: true,
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.fromLTRB(16, 0, 16, 0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              const SizedBox(height: 16),
              Card(
                elevation: 2,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(16),
                ),
                child: Padding(
                  padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
                  child: TextField(
                    controller: _ingredientController,
                    decoration: const InputDecoration(
                      hintText: 'Type in ingredients separated by a comma...',
                      border: InputBorder.none,
                    ),
                    style: theme.textTheme.bodyMedium,
                    maxLines: 5,
                  ),
                ),
              ),
              const SizedBox(height: 24),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  Expanded(
                    child: ElevatedButton.icon(
                      onPressed: _pickImage,
                      icon: const Icon(Icons.photo_library),
                      label: const Text('Gallery'),
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: ElevatedButton.icon(
                      onPressed: _navigateToLiveScan,
                      icon: const Icon(Icons.camera_alt),
                      label: const Text('Scan'),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 24),
              ElevatedButton(
                onPressed: _handleIngredients,
                child: const Text('Analyze Ingredients'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:flutter/services.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import '../services/database_service.dart';

class LiveScanPage extends StatefulWidget {
  const LiveScanPage({super.key});

  @override
  State<LiveScanPage> createState() => _LiveScanPageState();
}

class _LiveScanPageState extends State<LiveScanPage> {
  late CameraController _cameraController;
  late TextRecognizer _textRecognizer;
  bool _isCameraInitialized = false;
  bool _isProcessing = false;
  final Set<String> _detectedIngredients = {};
  final Map<String, dynamic> _ingredientDetails = {};
  List<String> _textBuffer = [];
  DateTime _lastProcessTime = DateTime.now();
  bool _genericSulfateOrSulfonateDetected = false;

  @override
  void initState() {
    super.initState();
    _initializeCamera();
    _textRecognizer = TextRecognizer(script: TextRecognitionScript.latin);
  }

  @override
  void dispose() {
    _cameraController.dispose();
    _textRecognizer.close();
    super.dispose();
  }

  Future<void> _initializeCamera() async {
    final cameras = await availableCameras();
    _cameraController = CameraController(cameras[0], ResolutionPreset.medium,
        enableAudio: false);

    await _cameraController.initialize();
    if (!mounted) return;

    setState(() => _isCameraInitialized = true);
    _startImageStream();
  }

  void _startImageStream() {
    _cameraController.startImageStream(_processCameraImage);
  }

  Future<void> _processCameraImage(CameraImage image) async {
    if (_isProcessing) return;
    _isProcessing = true;

    try {
      final inputImage = _prepareInputImage(image);
      final recognizedText = await _textRecognizer.processImage(inputImage);

      _addToTextBuffer(recognizedText.text);

      if (DateTime.now().difference(_lastProcessTime).inSeconds >= 1) {
        await _processTextBuffer();
        _lastProcessTime = DateTime.now();
      }
    } catch (e) {
      print('Error processing image: $e');
    } finally {
      _isProcessing = false;
    }
  }

  InputImage _prepareInputImage(CameraImage image) {
    final WriteBuffer allBytes = WriteBuffer();
    for (final Plane plane in image.planes) {
      allBytes.putUint8List(plane.bytes);
    }
    final bytes = allBytes.done().buffer.asUint8List();

    final imageSize = Size(image.width.toDouble(), image.height.toDouble());
    const imageRotation = InputImageRotation.rotation0deg;
    const inputImageFormat = InputImageFormat.yuv420;

    final inputImageData = InputImageMetadata(
      size: imageSize,
      rotation: imageRotation,
      format: inputImageFormat,
      bytesPerRow: image.planes[0].bytesPerRow,
    );

    return InputImage.fromBytes(
      bytes: bytes,
      metadata: inputImageData,
    );
  }

  void _addToTextBuffer(String text) {
    _textBuffer.addAll(text.split(RegExp(r'[,\s]+')));
    _textBuffer = _textBuffer
        .map((word) => word.trim().toLowerCase())
        .where((word) => word.isNotEmpty)
        .toList();
  }

  Future<void> _processTextBuffer() async {
    if (_textBuffer.isEmpty) return;

    final newIngredients = _textBuffer.toSet().difference(_detectedIngredients);
    if (newIngredients.isNotEmpty) {
      final details = await DatabaseHelper.instance
          .fetchIngredientsDetails(newIngredients.toList());
      setState(() {
        for (final detail in details) {
          final name = detail['ingredient_name'] as String;
          _detectedIngredients.add(name.toLowerCase());
          _ingredientDetails[name.toLowerCase()] = detail;
        }

        // Check for generic sulfates or sulfonates
        for (final ingredient in newIngredients) {
          if (!_detectedIngredients.contains(ingredient.toLowerCase()) &&
              (ingredient.toLowerCase().contains('sulfate') ||
                  ingredient.toLowerCase().contains('sulfonate'))) {
            _genericSulfateOrSulfonateDetected = true;
            break;
          }
        }
      });
    }

    _textBuffer.clear();
  }

  @override
  Widget build(BuildContext context) {
    if (!_isCameraInitialized) {
      return const Center(child: CircularProgressIndicator());
    }
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(
          "Ingredient Scan",
          style: theme.textTheme.headlineSmall?.copyWith(
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      body: Column(
        children: [
          Expanded(flex: 3, child: _buildCameraPreview()),
          Expanded(flex: 2, child: _buildIngredientList()),
        ],
      ),
    );
  }

  Widget _buildCameraPreview() {
    return ClipRRect(
      borderRadius: BorderRadius.circular(16),
      child: AspectRatio(
        aspectRatio: 3 / 4,
        child: CameraPreview(_cameraController),
      ),
    );
  }

  Widget _buildIngredientList() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        Text('Detected Ingredients:',
            style: Theme.of(context).textTheme.titleLarge),
        const SizedBox(height: 8),
        ..._detectedIngredients.map((ingredient) => ListTile(
              title: Text(ingredient),
            )),
        ElevatedButton(
          onPressed: () {
            Navigator.pop(context, {
              'ingredients': _detectedIngredients.toList(),
              'genericSulfateOrSulfonateDetected':
                  _genericSulfateOrSulfonateDetected,
            });
          },
          child: const Text('Done'),
        ),
      ],
    );
  }
}

import 'dart:io';
import 'package:curlify/src/services/ad_helper.dart';
import 'package:flutter/material.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:image_picker/image_picker.dart';
import '../services/database_service.dart';
import 'result_page.dart';

class RecognitionPage extends StatefulWidget {
  const RecognitionPage({super.key, required this.xFile});

  final XFile xFile;

  @override
  State<RecognitionPage> createState() => _RecognitionPageState();
}

class _RecognitionPageState extends State<RecognitionPage> {
  final InterstitialAdManager _interstitialAdManager = InterstitialAdManager();
  bool _isScanning = false;
  String _scannedText = '';
  final bool _isList = false;

  Future<void> _getRecognizedText() async {
    if (!mounted) return;

    setState(() => _isScanning = true);

    try {
      final inputImage = InputImage.fromFilePath(widget.xFile.path);
      final textRecognizer =
          TextRecognizer(script: TextRecognitionScript.latin);

      final RecognizedText recognizedText =
          await textRecognizer.processImage(inputImage);

      if (!mounted) return;

      setState(() {
        _scannedText = recognizedText.blocks
            .expand((block) => block.lines)
            .map((line) => line.text)
            .join(_isList ? '\n' : ' ');
      });

      textRecognizer.close();
      await _processScannedText();
    } catch (e) {
      _showErrorSnackBar('Error recognizing text: $e');
    } finally {
      if (mounted) setState(() => _isScanning = false);
    }
  }

  Future<void> _processScannedText() async {
    if (!mounted) return;

    String cleanedText = _scannedText.replaceAll(
        RegExp(r'[0-9]|Image ID:|www\.\S+|\s{2,}|QUESTIONS?.*'), '');

    final ingredientsText = cleanedText
        .split(',')
        .map((s) => s.trim())
        .where((s) => s.isNotEmpty)
        .toList();

    if (ingredientsText.isEmpty) {
      _showErrorSnackBar('No valid ingredients detected, please try again.');
      return;
    }

    try {
      final details = await DatabaseHelper.instance
          .fetchIngredientsDetails(ingredientsText);
      if (!mounted) return;

      await Navigator.pushReplacement(
        context,
        MaterialPageRoute(
          builder: (context) => ResultPage(
            genericSulfateOrSulfonateDetected: false,
            ingredientsDetails: details,
            adManager: _interstitialAdManager,
          ),
        ),
      );
    } catch (e) {
      _showErrorSnackBar('Error fetching ingredient details: $e');
    }
  }

  void _showErrorSnackBar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text(
          "Selected Image",
          style: theme.textTheme.headlineSmall?.copyWith(
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _getRecognizedText,
        label: const Text('Scan'),
        icon: const Icon(Icons.check_outlined),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(12.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            ClipRRect(
              borderRadius: BorderRadius.circular(12.0),
              child: ConstrainedBox(
                constraints: const BoxConstraints(maxHeight: 500),
                child: Image.file(
                  File(widget.xFile.path),
                  width: double.infinity,
                  fit: BoxFit.contain,
                ),
              ),
            ),
            if (_isScanning) const Center(child: CircularProgressIndicator()),
          ],
        ),
      ),
    );
  }
}

import 'package:flutter/material.dart';
import 'package:typicons_flutter/typicons_flutter.dart';
import '../services/ad_helper.dart';

class ResultPage extends StatefulWidget {
  final List<Map<String, dynamic>> ingredientsDetails;
  final InterstitialAdManager adManager;
  final bool genericSulfateOrSulfonateDetected;

  const ResultPage({
    super.key,
    required this.ingredientsDetails,
    required this.adManager,
    required this.genericSulfateOrSulfonateDetected,
  });

  @override
  ResultPageState createState() => ResultPageState();
}

class ResultPageState extends State<ResultPage> {
  @override
  Widget build(BuildContext context) {
    final hasCationicIngredient =
        widget.ingredientsDetails.any((item) => item['is_cationic'] == 1);
    final hasDimethicone = widget.ingredientsDetails.any(
        (item) => _isDimethicone(_capitalizeWords(item['ingredient_name'])));
    final hasRelationship = hasCationicIngredient && hasDimethicone;

    final List<Map<String, dynamic>> relationshipIngredients = hasRelationship
        ? widget.ingredientsDetails
            .where((ingredient) =>
                ingredient['is_cationic'] == 1 ||
                _isDimethicone(_capitalizeWords(ingredient['ingredient_name'])))
            .toList()
        : [];

    final List<Map<String, dynamic>> normalIngredients = hasRelationship
        ? widget.ingredientsDetails
            .where((ingredient) =>
                ingredient['is_cationic'] != 1 &&
                !_isDimethicone(
                    _capitalizeWords(ingredient['ingredient_name'])))
            .toList()
        : widget.ingredientsDetails;

    final sortedNormalIngredients = _sortIngredients(normalIngredients);
    final overallStatus =
        _getOverallStatus(widget.ingredientsDetails, hasRelationship);

    return PopScope(
      canPop: false,
      onPopInvoked: (didPop) {
        if (didPop) return;
        _handleBackNavigation();
      },
      child: Scaffold(
        body: SafeArea(
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Row(
                  children: [
                    IconButton(
                      icon: const Icon(Icons.arrow_back),
                      onPressed: _handleBackNavigation,
                    ),
                    const Expanded(
                      child: Text(
                        "Results",
                        style: TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.bold,
                        ),
                        textAlign: TextAlign.center,
                      ),
                    ),
                    const SizedBox(width: 48), // To balance the back button
                  ],
                ),
              ),
              _buildStatusBanner(overallStatus),
              Expanded(
                child: ListView(
                  children: [
                    if (hasRelationship)
                      _buildRelationshipSection(
                          context, relationshipIngredients),
                    ..._buildIngredientList(context, sortedNormalIngredients),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _handleBackNavigation() {
    widget.adManager.showAdIfLoaded(
      context,
      () {
        if (!mounted) return;
        Navigator.of(context)
            .pop(); // This will now happen after the ad is closed
      },
    );
  }

  Widget _buildStatusBanner(String status) {
    Color color;
    IconData icon;
    switch (status) {
      case 'Not Approved':
        color = Colors.red[400]!;
        icon = Typicons.times;
        break;
      case 'Approved with Caution':
        color = Colors.orange[300]!;
        icon = Typicons.warning;
        break;
      case 'Approved':
        color = Colors.green[400]!;
        icon = Typicons.tick;
        break;
      default:
        color = Colors.grey;
        icon = Icons.help;
    }

    return Container(
      color: color,
      padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
      child: Row(
        children: [
          Icon(icon, color: Colors.white),
          const SizedBox(width: 8),
          Text(
            status,
            style: const TextStyle(
                color: Colors.white, fontWeight: FontWeight.bold),
          ),
        ],
      ),
    );
  }

  List<Widget> _buildIngredientList(
      BuildContext context, List<Map<String, dynamic>> sortedIngredients) {
    List<Widget> widgets = [];
    String currentStatus = '';

    // Add generic sulfate/sulfonate if detected
    if (widget.genericSulfateOrSulfonateDetected) {
      sortedIngredients.insert(0, {
        'ingredient_name': 'Generic Sulfate / Sulfonate',
        'ingredient_type': 'Sulfate/Sulfonate',
        'cg_approval_status': 'No',
        'notes':
            'Generic sulfate or sulfonate detected. These are typically not approved for curly hair care.',
      });
    }

    for (var ingredient in sortedIngredients) {
      if (ingredient['cg_approval_status'] != currentStatus) {
        currentStatus = ingredient['cg_approval_status'];
        final ingredientsInCategory = sortedIngredients
            .where((i) => i['cg_approval_status'] == currentStatus)
            .toList();

        if (ingredientsInCategory.isNotEmpty) {
          if (widgets.isNotEmpty) {
            widgets.add(_buildCustomDivider());
          }
          widgets.add(
            Padding(
              padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
              child: Text(
                _getStatusText(currentStatus),
                style: const TextStyle(fontWeight: FontWeight.bold),
              ),
            ),
          );
        }
      }

      widgets.add(_buildIngredientTile(context, ingredient));
    }

    return widgets;
  }

  Widget _buildCustomDivider() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16.0),
      child: Container(
        height: 1,
        color: Colors.grey[300],
      ),
    );
  }

  Widget _buildRelationshipSection(
      BuildContext context, List<Map<String, dynamic>> ingredients) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Padding(
          padding: EdgeInsets.all(16.0),
          child: Text(
            'The following ingredients are not approved due to their relationship, which may cause build-up',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
        Container(
          margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
          decoration: BoxDecoration(
            color: Colors.transparent,
            borderRadius: BorderRadius.circular(12),
          ),
          child: Card(
            margin: EdgeInsets.zero,
            elevation: 0,
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(12),
                side: const BorderSide(
                  color: Color.fromARGB(255, 241, 107, 107),
                  width: 2,
                )),
            color: Colors.transparent,
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  ...ingredients.map((ingredient) =>
                      _buildIngredientTile(context, ingredient)),
                ],
              ),
            ),
          ),
        ),
      ],
    );
  }

  Widget _buildIngredientTile(
      BuildContext context, Map<String, dynamic> ingredient) {
    return ListTile(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16.0),
      ),
      contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 0),
      leading: Container(
        width: 50,
        height: 50,
        decoration: BoxDecoration(
          color: _getColorForIngredientType(ingredient['ingredient_type']),
          borderRadius: BorderRadius.circular(8),
        ),
        alignment: Alignment.center,
        child: Text(
          _getAbbreviationForType(ingredient['ingredient_type']),
          style: const TextStyle(
              color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
        ),
      ),
      title: Text(_capitalizeWords(
          ingredient['ingredient_name'] ?? 'Unknown Ingredient')),
      subtitle: Text(ingredient['ingredient_type'] ?? 'Unknown Type'),
      onTap: () => _showNotesDialog(context, ingredient),
    );
  }

  bool _isDimethicone(String? name) {
    if (name == null) return false;
    String lowercaseName = name.toLowerCase();
    return lowercaseName == 'dimethicone' ||
        lowercaseName == 'dimethicone copolyol';
  }

  List<Map<String, dynamic>> _sortIngredients(
      List<Map<String, dynamic>> ingredients) {
    return List<Map<String, dynamic>>.from(ingredients)
      ..sort((a, b) {
        int statusOrder(String? status) {
          switch (status) {
            case 'No':
              return 0;
            case 'Caution':
              return 1;
            case 'Yes':
              return 2;
            default:
              return 3;
          }
        }

        return statusOrder(a['cg_approval_status'])
            .compareTo(statusOrder(b['cg_approval_status']));
      });
  }

  String _getOverallStatus(
      List<Map<String, dynamic>> ingredients, bool hasRelationship) {
    if (hasRelationship || widget.genericSulfateOrSulfonateDetected) {
      return 'Not Approved';
    } else if (ingredients.any((i) => i['cg_approval_status'] == 'No')) {
      return 'Not Approved';
    } else if (ingredients.any((i) => i['cg_approval_status'] == 'Caution')) {
      return 'Approved with Caution';
    } else {
      return 'Approved';
    }
  }

  String _getAbbreviationForType(String? type) {
    if (type == null) return "??";
    List<String> words = type.split(' ');
    if (words.length == 1) {
      return type.substring(0, type.length >= 2 ? 2 : 1);
    }
    return (words[0][0] + words[1][0]);
  }

  Color _getColorForIngredientType(String? type) {
    switch (type?.toLowerCase()) {
      case 'solvent':
        return Colors.blue[300]!;
      case 'oil':
        return Colors.green[300]!;
      case 'humectant':
        return Colors.purple[300]!;
      case 'emollient':
        return Colors.orange[300]!;
      case 'fragrance':
        return Colors.pink[300]!;
      case 'emulsifier':
        return Colors.teal[300]!;
      case 'butter':
        return Colors.yellow[400]!;
      case 'occlusive agent':
        return Colors.brown[400]!;
      case 'preservative':
        return Colors.red[300]!;
      case 'extract':
        return Colors.lightGreen[300]!;
      case 'protein':
        return Colors.deepOrange[300]!;
      case 'polyquat':
        return Colors.cyan[300]!;
      case 'surfactant':
        return Colors.indigo[300]!;
      case 'vitamin':
        return Colors.amber[200]!;
      case 'film former':
        return Colors.lime[200]!;
      case 'sulfate':
        return Colors.deepPurple[300]!;
      default:
        return Colors.grey[400]!;
    }
  }

  void _showNotesDialog(BuildContext context, Map<String, dynamic> ingredient) {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text(_capitalizeWords(ingredient['ingredient_name'])),
          content: Text(
            ingredient['notes'] ?? 'No additional information available.',
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(context).pop(),
              child: const Text('OK'),
            ),
          ],
        );
      },
    );
  }

  String _capitalizeWords(String text) {
    return text.split(' ').map((word) {
      if (word.startsWith('(') && word.length > 1) {
        return '(${word[1].toUpperCase()}${word.substring(2).toLowerCase()}';
      }
      return word.isNotEmpty
          ? word[0].toUpperCase() + word.substring(1).toLowerCase()
          : word;
    }).join(' ');
  }

  String _getStatusText(String status) {
    switch (status) {
      case 'No':
        return 'Not Approved';
      case 'Caution':
        return 'Approved with Caution';
      case 'Yes':
        return 'Approved';
      default:
        return 'Other Ingredients';
    }
  }
}

import 'dart:io';
import 'package:curlify/src/pages/ad_overlay.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

class AdHelper {
  static String get interstitialAdUnitId {
    if (Platform.isAndroid) {
      return 'ca-app-pub-3940256099942544/1033173712'; // Test ad unit ID for Android
    } else if (Platform.isIOS) {
      return 'ca-app-pub-3940256099942544/4411468910'; // Test ad unit ID for iOS
    } else {
      throw UnsupportedError('Unsupported platform');
    }
  }

  static Future<void> initAds() async {
    await MobileAds.instance.initialize();
  }
}

class InterstitialAdManager {
  InterstitialAd? _interstitialAd;
  bool _isAdLoaded = false;
  bool _isAdLoading = false;

  void loadAd() {
    if (_isAdLoading) return;
    _isAdLoading = true;

    InterstitialAd.load(
      adUnitId: AdHelper.interstitialAdUnitId,
      request: const AdRequest(),
      adLoadCallback: InterstitialAdLoadCallback(
        onAdLoaded: (ad) {
          _interstitialAd = ad;
          _isAdLoaded = true;
          _isAdLoading = false;
          print('Interstitial ad loaded successfully.');
        },
        onAdFailedToLoad: (error) {
          print('InterstitialAd failed to load: $error');
          _isAdLoading = false;
          _isAdLoaded = false;
          _interstitialAd = null;
          Future.delayed(const Duration(minutes: 1), loadAd);
        },
      ),
    );
  }

  Future<void> showAdIfLoaded(
      BuildContext context, VoidCallback onAdClosed) async {
    if (_isAdLoaded && _interstitialAd != null) {
      // Show overlay
      showDialog(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) => AdOverlay(onAdClosed: onAdClosed),
      );

      _interstitialAd!.fullScreenContentCallback = FullScreenContentCallback(
        onAdShowedFullScreenContent: (InterstitialAd ad) {
          SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
        },
        onAdDismissedFullScreenContent: (ad) {
          SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
          ad.dispose();
          _isAdLoaded = false;
          _interstitialAd = null;
          Navigator.of(context).pop(); // Dismiss overlay
          onAdClosed();
          loadAd(); // Load the next ad
        },
        onAdFailedToShowFullScreenContent: (ad, error) {
          print('Failed to show ad: $error');
          ad.dispose();
          _isAdLoaded = false;
          _interstitialAd = null;
          Navigator.of(context).pop(); // Dismiss overlay
          onAdClosed();
          loadAd();
        },
      );
      await _interstitialAd!.show();
    } else {
      print('Interstitial ad not ready yet. Proceeding without showing ad.');
      onAdClosed();
      loadAd();
    }
  }

  void dispose() {
    _interstitialAd?.dispose();
  }
}

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:csv/csv.dart';

class DatabaseHelper {
  static final DatabaseHelper instance = DatabaseHelper._init();
  static Database? _database;

  DatabaseHelper._init();

  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDB('ingredients.db');
    return _database!;
  }

  Future<Database> _initDB(String filePath) async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, filePath);

    return await openDatabase(
      path,
      version: 11,
      onCreate: _createDB,
      onUpgrade: _onUpgrade,
    );
  }

  Future<void> _createDB(Database db, int version) async {
    await db.execute('''
      CREATE TABLE ingredients (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        ingredient_name TEXT NOT NULL UNIQUE,
        ingredient_type TEXT NOT NULL,
        cg_approval_status TEXT,
        notes TEXT,
        is_cationic INTEGER NOT NULL
      )
    ''');

    await db.execute('''
      CREATE TABLE ingredient_aliases (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        ingredient_id INTEGER NOT NULL,
        alias TEXT NOT NULL UNIQUE,
        FOREIGN KEY (ingredient_id) REFERENCES ingredients (id)
      )
    ''');

    await db.execute(
        'CREATE INDEX idx_ingredient_name ON ingredients (ingredient_name)');
    await db.execute(
        'CREATE INDEX idx_ingredient_alias ON ingredient_aliases (alias)');

    await _importCSVData(db);
  }

  Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
    if (oldVersion < newVersion) {
      // Drop old tables
      await db.execute('DROP TABLE IF EXISTS ingredient_aliases');
      await db.execute('DROP TABLE IF EXISTS ingredients');

      // Recreate tables
      await _createDB(db, newVersion);
    }
  }

  Future<void> _importCSVData(Database db) async {
    try {
      final String csvData =
          await rootBundle.loadString('assets/ingredients.csv');
      final List<List<dynamic>> csvTable =
          const CsvToListConverter().convert(csvData);

      await db.transaction((txn) async {
        for (var i = 1; i < csvTable.length; i++) {
          final row = csvTable[i];
          final int id = await txn.rawInsert('''
            INSERT OR REPLACE INTO ingredients 
            (ingredient_name, ingredient_type, cg_approval_status, notes, is_cationic)
            VALUES (?, ?, ?, ?, ?)
          ''', [
            row[0],
            row[1],
            row[2],
            row[3],
            row[4] == 'true' ? 1 : 0,
          ]);

          // Add alias if it exists
          if (row.length > 5 && row[5] != null && row[5].isNotEmpty) {
            await txn.rawInsert('''
              INSERT OR REPLACE INTO ingredient_aliases
              (ingredient_id, alias)
              VALUES (?, ?)
            ''', [id, row[5]]);
          }
        }
      });
    } catch (e) {
      print('Error importing CSV data: $e');
    }
  }

  Future<List<Map<String, dynamic>>> fetchIngredientsDetails(
      List<String> ingredients) async {
    final db = await database;
    final lowercasedIngredients =
        ingredients.map((e) => e.toLowerCase()).toList();

    try {
      final List<Map<String, dynamic>> results = await db.rawQuery('''
      SELECT DISTINCT i.*
      FROM ingredients i
      WHERE LOWER(i.ingredient_name) IN (${List.filled(lowercasedIngredients.length, '?').join(',')})
      OR i.id IN (
        SELECT a.ingredient_id
        FROM ingredient_aliases a
        WHERE LOWER(a.alias) IN (${List.filled(lowercasedIngredients.length, '?').join(',')})
      )
    ''', [...lowercasedIngredients, ...lowercasedIngredients]);
      return results;
    } catch (e) {
      print('Error fetching ingredient details: $e');
      return [];
    }
  }

  Future<void> closeDatabase() async {
    final db = await instance.database;
    await db.close();
    _database = null;
  }

  Future<void> resetDatabase() async {
    final db = await instance.database;
    await db.transaction((txn) async {
      await txn.delete('ingredients');
      await txn.delete('ingredient_aliases');
      await _importCSVData(db);
    });
  }
}

Please adjust my code for the following changes:

2 dollars for ad removal, buyable via google play store, with restore purchase, the first click on startup and every second click shows implemented advert
Settings page for Design (system, dark, light), Upgrade to pro, terms of service (nutzungsbedingungen), Rate App 
Onboarding screen with explanation Info button and scanning tutorial and pretty animations (User should slowly scan for the ocr to pick up most of the items)
Scanning Page: Explanation similar to onboarding screen as a disclaimer, floating done button that is sticky, improved design for found items (tile based like in results)

BUG: Please enter one ingredient banner not disappearing and looking ugly