import 'dart:async';

import 'package:universal_html/html.dart' as html;


class PickedCsvFile {
  final String name;
  final String content;

  const PickedCsvFile({required this.name, required this.content});
}

abstract class CsvInputService {
  Future<PickedCsvFile?> pickCsv();
}

class WebCsvInputService implements CsvInputService {
  @override
  Future<PickedCsvFile?> pickCsv() async {
    final input = html.FileUploadInputElement()
      ..accept = '.csv,text/csv'
      ..multiple = false
      ..style.display = 'none';

    html.document.body?.append(input);
    final file = await _pickFile(input);
    if (file == null) return null;

    final reader = html.FileReader();
    final readCompleter = Completer<String>();
    reader.onError.listen((_) {
      readCompleter.completeError(StateError('Failed to read file'));
    });
    reader.onLoadEnd.listen((_) {
      final result = reader.result;
      if (result is String) {
        readCompleter.complete(result);
      } else {
        readCompleter.completeError(StateError('Unsupported file payload'));
      }
    });
    reader.readAsText(file);

    final content = await readCompleter.future;
    return PickedCsvFile(name: file.name, content: content);
  }

  Future<html.File?> _pickFile(html.FileUploadInputElement input) async {
    final completer = Completer<html.File?>();

    void completeWith(html.File? file) {
      if (!completer.isCompleted) completer.complete(file);
    }

    StreamSubscription<html.Event>? changeSub;
    StreamSubscription<html.Event>? inputSub;
    StreamSubscription<html.Event>? focusSub;
    Timer? timeout;

    void cleanup() {
      changeSub?.cancel();
      inputSub?.cancel();
      focusSub?.cancel();
      timeout?.cancel();
      input.remove();
    }

    void tryComplete() {
      final file = input.files?.isNotEmpty == true ? input.files!.first : null;
      if (file != null) {
        completeWith(file);
      }
    }

    changeSub = input.onChange.listen((_) => tryComplete());
    inputSub = input.onInput.listen((_) => tryComplete());
    focusSub = html.window.onFocus.listen((_) async {
      if (completer.isCompleted) return;
      for (var i = 0; i < 6; i++) {
        tryComplete();
        if (completer.isCompleted) return;
        await Future<void>.delayed(const Duration(milliseconds: 120));
      }
      if (!completer.isCompleted && (input.value ?? '').isEmpty) {
        completeWith(null);
      }
    });

    timeout = Timer(const Duration(minutes: 2), () => completeWith(null));

    completer.future.whenComplete(cleanup);
    input.click();
    return completer.future;
  }
}
