import 'dart:convert';

/// CSV 파일에서 이벤트 데이터를 파싱하는 유틸리티 클래스
/// 반환 형식은 EventExcelParser와 동일하게 맞춥니다.
class EventCsvParser {
  /// CSV 바이트에서 이벤트 데이터를 추출합니다.
  /// 헤더는 첫 줄로 가정하며, 필수 컬럼은 title, startDate 입니다.
  /// 반환값 키:
  /// - processedRows, validEvents, invalidRows, invalidRowIndices, error
  static Map<String, dynamic> parseCsvBytes(List<int> bytes) {
    final content = utf8.decode(bytes, allowMalformed: true);
    final lines = _splitLines(content)
        .where((e) => e.trim().isNotEmpty)
        .toList();

    if (lines.isEmpty) {
      return {
        'processedRows': 0,
        'validEvents': <Map<String, dynamic>>[],
        'invalidRows': 0,
        'invalidRowIndices': <int>[],
        'error': 'CSV 파일에 데이터가 없습니다.'
      };
    }

    final headersRaw = _parseCsvLine(lines.first);
    final headers = headersRaw
        .map((e) => e.trim())
        .toList();

    // 한글 헤더를 영문 키로 정규화하기 위한 매핑
    String _normalizeHeader(String raw) {
      final v = raw.trim().toLowerCase();
      // 공백 제거 버전도 별도로 준비
      final vn = v.replaceAll(' ', '');
      // 한국어 -> 영문 매핑
      if (v == '제목') return 'title';
      if (v == '시작일') return 'startDate';
      if (v == '종료일') return 'endDate';
      if (v == '설명') return 'description';
      if (v == '장소') return 'location';
      if (v == '상태') return 'status';
      if (v == '유형') return 'type';
      if (v == '게임 수' || vn == '게임수') return 'gameCount';
      if (v == '참가비') return 'participantFee';
      if (v == '신청마감' || v == '신청 마감') return 'registrationDeadline';
      if (v == '최대 참가자' || vn == '최대참가자') return 'maxParticipants';

      // 기존 영문 키들 정규화
      if (vn == 'maxparticipants') return 'maxParticipants';
      if (vn == 'startdate') return 'startDate';
      if (vn == 'enddate') return 'endDate';
      return raw.trim();
    }

    final normalizedHeaders = headers.map(_normalizeHeader).toList();

    if (!normalizedHeaders.contains('title') || !normalizedHeaders.contains('startDate')) {
      return {
        'processedRows': 0,
        'validEvents': <Map<String, dynamic>>[],
        'invalidRows': 0,
        'invalidRowIndices': <int>[],
        'error': '필수 필드(title, startDate)가 없습니다.'
      };
    }

    final events = <Map<String, dynamic>>[];
    int processedRows = 0;
    int invalidRows = 0;
    final invalidRowIndices = <int>[];
    final invalidRowReasons = <int, String>{};

    for (var i = 1; i < lines.length; i++) {
      final row = _parseCsvLine(lines[i]);
      if (row.isEmpty || row.every((c) => c.trim().isEmpty)) {
        // 빈 행은 스킵
        continue;
      }
      processedRows++;

      final event = <String, dynamic>{};
      for (var j = 0; j < normalizedHeaders.length && j < row.length; j++) {
        final header = normalizedHeaders[j];
        var value = row[j].trim();

        if (header == 'startDate' || header == 'endDate') {
          // 날짜 문자열은 그대로 전달하고, 화면/서버 측에서 ISO 변환/검증 처리
          // 비어있으면 startDate는 필수, endDate는 null
          if (header == 'startDate') {
            event['startDate'] = value.isEmpty ? DateTime.now().toIso8601String() : value;
          } else {
            event['endDate'] = value.isEmpty ? null : value;
          }
        } else {
          event[header] = value.isEmpty ? null : value;
        }
      }

      final titleVal = (event['title'] ?? '').toString().trim();
      final startVal = (event['startDate'] ?? '').toString().trim();
      if (titleVal.isNotEmpty && startVal.isNotEmpty) {
        events.add(event);
      } else {
        invalidRows++;
        final rowNo = i + 1;
        invalidRowIndices.add(rowNo); // 헤더가 1행이므로 +1
        final reasons = <String>[];
        if (titleVal.isEmpty) reasons.add('title 누락');
        if (startVal.isEmpty) reasons.add('startDate 누락');
        invalidRowReasons[rowNo] = reasons.join(', ');
      }
    }

    return {
      'processedRows': processedRows,
      'validEvents': events,
      'invalidRows': invalidRows,
      'invalidRowIndices': invalidRowIndices,
      'invalidRowReasons': invalidRowReasons,
      'error': events.isEmpty ? '유효한 이벤트 데이터가 없습니다.' : null
    };
  }

  // 간단한 CSV 라인 파서: 따옴표 포함 필드와 이스케이프된 따옴표("") 처리
  static List<String> _parseCsvLine(String line) {
    final result = <String>[];
    final buf = StringBuffer();
    bool inQuotes = false;

    for (int i = 0; i < line.length; i++) {
      final ch = line[i];
      if (inQuotes) {
        if (ch == '"') {
          // 이스케이프된 따옴표
          if (i + 1 < line.length && line[i + 1] == '"') {
            buf.write('"');
            i++;
          } else {
            inQuotes = false;
          }
        } else {
          buf.write(ch);
        }
      } else {
        if (ch == ',') {
          result.add(buf.toString());
          buf.clear();
        } else if (ch == '"') {
          inQuotes = true;
        } else {
          buf.write(ch);
        }
      }
    }
    result.add(buf.toString());
    return result;
  }

  static List<String> _splitLines(String content) {
    return content.replaceAll('\r\n', '\n').replaceAll('\r', '\n').split('\n');
  }
}
