import 'package:pweb/models/payment/multiple_payouts/csv_row.dart';


class MultipleCsvParser {
  List<CsvPayoutRow> parseRows(String content) {
    final lines = content
        .replaceAll('\r\n', '\n')
        .replaceAll('\r', '\n')
        .split('\n')
        .where((line) => line.trim().isNotEmpty)
        .toList(growable: false);

    if (lines.isEmpty) {
      throw FormatException('CSV is empty');
    }

    final delimiter = _detectDelimiter(lines.first);
    final header = _parseCsvLine(
      lines.first,
      delimiter,
    ).map((value) => value.trim().toLowerCase()).toList(growable: false);

    final panIndex = _resolveHeaderIndex(header, const ['pan', 'card_pan']);
    final firstNameIndex = _resolveHeaderIndex(header, const [
      'first_name',
      'firstname',
    ]);
    final lastNameIndex = _resolveHeaderIndex(header, const [
      'last_name',
      'lastname',
    ]);
    final expDateIndex = _resolveHeaderIndex(header, const [
      'exp_date',
      'expiry',
      'expiry_date',
    ]);
    final expMonthIndex = _resolveHeaderIndex(header, const [
      'exp_month',
      'expiry_month',
    ]);
    final expYearIndex = _resolveHeaderIndex(header, const [
      'exp_year',
      'expiry_year',
    ]);
    final amountIndex = _resolveHeaderIndex(header, const ['amount', 'sum']);
    final commentIndex = _resolveHeaderIndex(header, const ['comment']);

    if (panIndex < 0 ||
        firstNameIndex < 0 ||
        lastNameIndex < 0 ||
        (expDateIndex < 0 && (expMonthIndex < 0 || expYearIndex < 0)) ||
        amountIndex < 0) {
      throw FormatException(
        'CSV header must contain pan, first_name, last_name, amount columns and either exp_date/expiry or exp_month and exp_year',
      );
    }

    final rows = <CsvPayoutRow>[];
    for (var i = 1; i < lines.length; i++) {
      final raw = _parseCsvLine(lines[i], delimiter);
      final pan = _cell(raw, panIndex);
      final firstName = _cell(raw, firstNameIndex);
      final lastName = _cell(raw, lastNameIndex);
      final expDateRaw = expDateIndex >= 0 ? _cell(raw, expDateIndex) : '';
      final expMonthRaw = _cell(raw, expMonthIndex);
      final expYearRaw = _cell(raw, expYearIndex);
      final amount = _normalizeAmount(_cell(raw, amountIndex));
      final comment = commentIndex >= 0 ? _cell(raw, commentIndex) : '';

      if (pan.isEmpty) {
        throw FormatException('CSV row ${i + 1}: pan is required');
      }
      if (firstName.isEmpty) {
        throw FormatException('CSV row ${i + 1}: first_name is required');
      }
      if (lastName.isEmpty) {
        throw FormatException('CSV row ${i + 1}: last_name is required');
      }
      if (amount.isEmpty) {
        throw FormatException('CSV row ${i + 1}: amount is required');
      }

      final parsedAmount = double.tryParse(amount);
      if (parsedAmount == null || parsedAmount <= 0) {
        throw FormatException(
          'CSV row ${i + 1}: amount must be greater than 0',
        );
      }

      int expMonth;
      int expYear;
      if (expDateIndex >= 0 && expDateRaw.isNotEmpty) {
        final parsed = _parseExpiryDate(expDateRaw, i + 1);
        expMonth = parsed.month;
        expYear = parsed.year;
      } else if (expMonthIndex >= 0 && expYearIndex >= 0) {
        final parsedMonth = int.tryParse(expMonthRaw);
        if (parsedMonth == null || parsedMonth < 1 || parsedMonth > 12) {
          throw FormatException('CSV row ${i + 1}: exp_month must be 1-12');
        }
        final parsedYear = int.tryParse(expYearRaw);
        if (parsedYear == null || parsedYear < 0) {
          throw FormatException('CSV row ${i + 1}: exp_year is invalid');
        }
        expMonth = parsedMonth;
        expYear = parsedYear;
      } else {
        throw FormatException('CSV row ${i + 1}: exp_date is required');
      }

      rows.add(
        CsvPayoutRow(
          pan: pan,
          firstName: firstName,
          lastName: lastName,
          expMonth: expMonth,
          expYear: expYear,
          amount: amount,
          comment: _normalizeComment(comment),
        ),
      );
    }

    if (rows.isEmpty) {
      throw FormatException('CSV does not contain payout rows');
    }

    return rows;
  }

  int _resolveHeaderIndex(List<String> header, List<String> candidates) {
    for (final key in candidates) {
      final idx = header.indexOf(key);
      if (idx >= 0) return idx;
    }
    return -1;
  }

  String _detectDelimiter(String line) {
    final commaCount = _countUnquoted(line, ',');
    final semicolonCount = _countUnquoted(line, ';');
    if (semicolonCount > commaCount) return ';';
    return ',';
  }

  int _countUnquoted(String line, String needle) {
    var count = 0;
    var inQuotes = false;

    for (var i = 0; i < line.length; i++) {
      final char = line[i];
      if (char == '"') {
        final isEscaped = inQuotes && i + 1 < line.length && line[i + 1] == '"';
        if (isEscaped) {
          i++;
        } else {
          inQuotes = !inQuotes;
        }
        continue;
      }
      if (char == needle && !inQuotes) {
        count++;
      }
    }
    return count;
  }

  List<String> _parseCsvLine(String line, String delimiter) {
    final values = <String>[];
    final buffer = StringBuffer();
    var inQuotes = false;

    for (var i = 0; i < line.length; i++) {
      final char = line[i];

      if (char == '"') {
        final isEscaped = inQuotes && i + 1 < line.length && line[i + 1] == '"';
        if (isEscaped) {
          buffer.write('"');
          i++;
        } else {
          inQuotes = !inQuotes;
        }
        continue;
      }

      if (char == delimiter && !inQuotes) {
        values.add(buffer.toString());
        buffer.clear();
        continue;
      }

      buffer.write(char);
    }

    values.add(buffer.toString());
    return values;
  }

  String _cell(List<String> row, int index) {
    if (index < 0 || index >= row.length) return '';
    return row[index].trim();
  }

  String _normalizeAmount(String value) {
    return value.trim().replaceAll(' ', '').replaceAll(',', '.');
  }

  String? _normalizeComment(String value) {
    final normalized = value.trim();
    return normalized.isEmpty ? null : normalized;
  }

  _ExpiryDate _parseExpiryDate(String value, int rowNumber) {
    final match = RegExp(r'^\s*(\d{1,2})\s*/\s*(\d{2})\s*$').firstMatch(value);
    if (match == null) {
      throw FormatException(
        'CSV row $rowNumber: exp_date must be in MM/YY format',
      );
    }
    final month = int.parse(match.group(1)!);
    final year = int.parse(match.group(2)!);
    if (month < 1 || month > 12) {
      throw FormatException('CSV row $rowNumber: exp_date month must be 1-12');
    }
    return _ExpiryDate(month, year);
  }
}

class _ExpiryDate {
  final int month;
  final int year;

  const _ExpiryDate(this.month, this.year);
}
