import 'dart:io';

import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart' as path_provide;
import 'package:path/path.dart' as path;

class LogUtil {
  static _Logger? _singleton;
  static bool _enable = false;

  static bool get enable => _enable;

  static Future<void> setEnable(bool val) async {
    _enable = val;
    if (_enable) {
      init();
    }
  }

  static Future<void> init() async {
    if (_singleton == null) {
      final instance = _Logger();
      await instance.init();
      _singleton = instance;
    }
  }

  static Future<void> dispose() async {
    await _singleton?.dispose();
    _singleton = null;
  }

  static void write(String content) {
    if (!enable) {
      return;
    }
    if (_singleton != null) {
      Future.microtask(() {
        _singleton?.write(content);
        _checkFileSize();
      });
    }
  }

  static void _checkFileSize() async {
    if (_singleton!.lineCount > 9999) {
      _singleton!.write("***Lines too many, ready to create new one***");
      final instance = _Logger();
      await instance.init();
      _singleton = instance;
      _singleton!.write("***New log which after the last big one***");
    }
  }
}

class _Logger {
  // ignore: constant_identifier_names
  static const _DIR = "device_plugin";

  IOSink? _ioSink;
  late final String _fileName;

  int lineCount = 0;

  Future<void> init() async {
    Directory? dir = await path_provide.getExternalStorageDirectory();
    dir ??= await path_provide.getApplicationDocumentsDirectory();
    final now = DateTime.now();
    final logTag = DateFormat('yyyyMMddHHmmssSSS').format(now);
    final fullDir = path.join(dir.path, _DIR);
    await _checkDir(fullDir);
    _fileName = path.join(fullDir, "$logTag.log");
  }

  Future<void> dispose() async {
    await _ioSink?.close();
    _ioSink = null;
  }

  void write(String content) async {
    _keepIOSink();

    try {
      final line = _buildLine(content);
      _ioSink!.writeln(line);
      lineCount++;
    } catch (e) {
      try {
        _ioSink!.close();
        _ioSink = null;
      } catch (e) {
        //
      }
    }
  }

  void _keepIOSink() {
    if (_ioSink == null) {
      final file = File(_fileName);
      _ioSink = file.openWrite(mode: FileMode.append);
    }
  }

  String _buildLine(String content) {
    final now = DateTime.now();
    final timeStr = DateFormat('yyyyMMddHHmmss.SSS').format(now);
    return "[$timeStr] $content";
  }

  Future<void> _checkDir(String dir) async {
    final directory = Directory(dir);
    final isExists = await directory.exists();
    if (!isExists) {
      try {
        await directory.create();
      } catch (e) {
        //
      }
    }
  }
}
