import 'dart:convert';
import 'dart:io';
import 'package:xml/xml.dart';
import 'package:flutter/foundation.dart';

// Copy of book abbreviations from BibleXmlParser
const Map<String, String> _bookAbbreviations = {
  'genesis': 'Gen',
  'exodus': 'Exod',
  'leviticus': 'Lev',
  'numbers': 'Num',
  'deuteronomy': 'Deut',
  'joshua': 'Josh',
  'judges': 'Judg',
  'ruth': 'Ruth',
  '1 samuel': '1Sam',
  '2 samuel': '2Sam',
  '1 kings': '1Kgs',
  '2 kings': '2Kgs',
  '1 chronicles': '1Chr',
  '2 chronicles': '2Chr',
  'ezra': 'Ezra',
  'nehemiah': 'Neh',
  'esther': 'Esth',
  'job': 'Job',
  'psalm': 'Ps',
  'proverbs': 'Prov',
  'ecclesiastes': 'Eccl',
  'song of solomon': 'Song',
  'isaiah': 'Isa',
  'jeremiah': 'Jer',
  'lamentations': 'Lam',
  'ezekiel': 'Ezek',
  'daniel': 'Dan',
  'hosea': 'Hos',
  'joel': 'Joel',
  'amos': 'Amos',
  'obadiah': 'Obad',
  'jonah': 'Jonah',
  'micah': 'Mic',
  'nahum': 'Nah',
  'habakkuk': 'Hab',
  'zephaniah': 'Zeph',
  'haggai': 'Hag',
  'zechariah': 'Zech',
  'malachi': 'Mal',
  'matthew': 'Matt',
  'mark': 'Mark',
  'luke': 'Luke',
  'john': 'John',
  'acts': 'Acts',
  'romans': 'Rom',
  '1 corinthians': '1Cor',
  '2 corinthians': '2Cor',
  'galatians': 'Gal',
  'ephesians': 'Eph',
  'philippians': 'Phil',
  'colossians': 'Col',
  '1 thessalonians': '1Thess',
  '2 thessalonians': '2Thess',
  '1 timothy': '1Tim',
  '2 timothy': '2Tim',
  'titus': 'Titus',
  'philemon': 'Phlm',
  'hebrews': 'Heb',
  'james': 'Jas',
  '1 peter': '1Pet',
  '2 peter': '2Pet',
  '1 john': '1John',
  '2 john': '2John',
  '3 john': '3John',
  'jude': 'Jude',
  'revelation': 'Rev',
};

// Map of translations to filenames
final Map<String, String> _translationFiles = {
  'esv': 'assets/bible_xml/ESV.xml',
  'niv': 'assets/bible_xml/NIV.xml',
  'nkjv': 'assets/bible_xml/NKJV.xml',
  'nlt': 'assets/bible_xml/NLT.xml',
  'nasb': 'assets/bible_xml/NASB.xml',
  'kjv': 'assets/bible_xml/KJV.xml',
  'msg': 'assets/bible_xml/MSG.xml',
};

void main() async {
  debugPrint('Starting asset optimization...');

  // 1. Load the base JSON
  final File jsonFile = File('assets/scriptures.json');
  if (!jsonFile.existsSync()) {
    debugPrint('Error: assets/scriptures.json not found.');
    return;
  }
  final Map<String, dynamic> data = json.decode(jsonFile.readAsStringSync());

  // 2. Load and Parse all XMLs
  final Map<String, XmlDocument> xmlDocs = {};
  for (var entry in _translationFiles.entries) {
    final key = entry.key;
    final path = entry.value;
    final file = File(path);
    if (file.existsSync()) {
      debugPrint('Parsing $key from $path...');
      try {
        xmlDocs[key] = XmlDocument.parse(file.readAsStringSync());
      } catch (e) {
        debugPrint('Error parsing $path: $e');
      }
    } else {
      debugPrint('Warning: $path not found.');
    }
  }

  // 3. Process the JSON structure
  for (var phase in ['menstrual', 'follicular', 'ovulation', 'luteal']) {
    if (data[phase] != null) {
      await _processList(data[phase] as List, xmlDocs);
    }
  }

  if (data['husband'] != null) {
    await _processList(data['husband'] as List, xmlDocs);
  }

  if (data['womanhood'] != null) {
    await _processList(data['womanhood'] as List, xmlDocs);
  }

  if (data['contextual'] != null) {
    final contextualMap = data['contextual'] as Map<String, dynamic>;
    for (var key in contextualMap.keys) {
      await _processList(contextualMap[key] as List, xmlDocs);
    }
  }

  // 4. Write the optimized JSON
  final outputFile = File('assets/scriptures_optimized.json');
  outputFile.writeAsStringSync(json.encode(data)); // Minified
  // outputFile.writeAsStringSync(const JsonEncoder.withIndent('  ').convert(data)); // Pretty print

  debugPrint(
      'Optimization complete. Wrote to assets/scriptures_optimized.json');
}

Future<void> _processList(List list, Map<String, XmlDocument> xmlDocs) async {
  for (var item in list) {
    final String reference = item['reference'];
    Map<String, dynamic> verses = item['verses'] ?? {};

    // Parse reference
    final parts = _parseReference(reference);
    if (parts == null) {
      debugPrint('Skipping invalid reference: $reference');
      continue;
    }

    // Look up for each translation
    for (var entry in _translationFiles.entries) {
      final transKey = entry.key; // esv, niv, etc.

      // If already has text, skip (or overwrite? Let's overwrite to ensure consistency,
      // but the original JSON had manual entries. The user wants to use the XMLs.
      // Let's only fill if missing or if we want to enforce XML source.)
      // Strategy: Fill if we have XML data.

      if (xmlDocs.containsKey(transKey)) {
        final text = _getVerseFromXml(xmlDocs[transKey]!, parts['book']!,
            int.parse(parts['chapter']!), int.parse(parts['verse']!));

        if (text != null) {
          verses[transKey] = text;
        } else {
          debugPrint('Warning: Could not find $reference in $transKey');
        }
      }
    }
    item['verses'] = verses;
  }
}

Map<String, String>? _parseReference(String reference) {
  final parts = reference.split(' ');
  if (parts.length < 2) return null;

  String book = parts.sublist(0, parts.length - 1).join(' ').toLowerCase();
  String chapterVerse = parts.last;
  final cvParts = chapterVerse.split(':');
  if (cvParts.length < 2) {
    return null; // Only handles simple Chapter:Verse for now
  }

  return {
    'book': book,
    'chapter': cvParts[0],
    // Handle cases like "1-2" by just taking the first one?
    // The current parser Logic only takes one verse number.
    // If the reference is "Psalm 23:1-2", the XML parser expects a single integer.
    // We should probably just parse the first verse or handle ranges?
    // For this fix, let's just parse the start verse to prevent crashing on int.parse
    'verse': cvParts[1].split('-')[0],
  };
}

String? _getVerseFromXml(
    XmlDocument document, String bookName, int chapterNum, int verseNum) {
  // Standardize book name for lookup
  String lookupBookName =
      _bookAbbreviations[bookName.toLowerCase()] ?? bookName;

  // Use root element to avoid full document search
  XmlElement root = document.rootElement;

  // -- Find Book --
  // Try Schema 1: Direct child of root
  var bookElement = root.findElements('BIBLEBOOK').firstWhere(
    (element) {
      final nameAttr = element.getAttribute('bname');
      return nameAttr?.toLowerCase() == lookupBookName.toLowerCase() ||
          nameAttr?.toLowerCase() == bookName.toLowerCase();
    },
    orElse: () => XmlElement(XmlName('notfound')),
  );

  // Try Schema 2 if not found
  if (bookElement.name.local == 'notfound') {
    bookElement = root.findElements('b').firstWhere(
      (element) {
        final nameAttr = element.getAttribute('n');
        return nameAttr?.toLowerCase() == lookupBookName.toLowerCase() ||
            nameAttr?.toLowerCase() == bookName.toLowerCase();
      },
      orElse: () => XmlElement(XmlName('notfound')),
    );
  }

  if (bookElement.name.local == 'notfound') {
    return null;
  }

  // -- Find Chapter --
  // Try Schema 1: Direct child of book
  var chapterElement = bookElement.findElements('CHAPTER').firstWhere(
        (element) => element.getAttribute('cnumber') == chapterNum.toString(),
        orElse: () => XmlElement(XmlName('notfound')),
      );

  // Try Schema 2 if not found
  if (chapterElement.name.local == 'notfound') {
    chapterElement = bookElement.findElements('c').firstWhere(
          (element) => element.getAttribute('n') == chapterNum.toString(),
          orElse: () => XmlElement(XmlName('notfound')),
        );
  }

  if (chapterElement.name.local == 'notfound') {
    return null;
  }

  // -- Find Verse --
  // Try Schema 1: Direct child of chapter
  var verseElement = chapterElement.findElements('VERS').firstWhere(
        (element) => element.getAttribute('vnumber') == verseNum.toString(),
        orElse: () => XmlElement(XmlName('notfound')),
      );

  // Try Schema 2 if not found
  if (verseElement.name.local == 'notfound') {
    verseElement = chapterElement.findElements('v').firstWhere(
          (element) => element.getAttribute('n') == verseNum.toString(),
          orElse: () => XmlElement(XmlName('notfound')),
        );
  }

  if (verseElement.name.local == 'notfound') {
    return null;
  }

  // Extract the text content of the verse
  return verseElement.innerText.trim();
}
