import 'dart:async';
import 'dart:io';
import 'package:platform_cache/platform_cache.dart';
import 'package:platform_foundation/core.dart';
import 'package:platform_testing/testing.dart';
import 'package:http/http.dart' as http;
//import 'package:glob/glob.dart';
import 'package:test/test.dart';
import 'package:logging/logging.dart';

Future<void> main() async {
  Logger.root.level = Level.ALL;
  Logger.root.onRecord.listen((record) {
    print(
        '${record.time}: ${record.level.name}: ${record.loggerName}: ${record.message}');
  });

  group('no timeout', () {
    late TestClient client;
    DateTime? lastModified;
    late http.Response response1, response2;

    setUp(() async {
      var app = Application();
      var cache = ResponseCache()
        ..patterns.addAll([
          //Glob('/*.txt'), // Requires to create folders and files for testing
          RegExp('^/?\\w+\\.txt'),
        ]);

      app.fallback(cache.handleRequest);

      app.get('/date.txt', (req, res) {
        var data = DateTime.now().toIso8601String();
        print('Res data: $data');
        res
          ..useBuffer()
          ..write(data);
        print('Generate results...');
      });

      app.addRoute('PURGE', '*', (req, res) {
        if (req.uri != null) {
          cache.purge(req.uri!.path);
          print('Purged ${req.uri!.path}');
        } else {
          print('req.uri is null');
        }
      });

      app.responseFinalizers.add(cache.responseFinalizer);

      var oldHandler = app.errorHandler;
      app.errorHandler = (e, req, res) {
        if (e.error == null) {
          oldHandler(e, req, res);
        }
        return Zone.current
            .handleUncaughtError(e.error as Object, e.stackTrace!);
      };

      client = await connectTo(app);
      response1 = await client.get(Uri.parse('/date.txt'));
      print('Response 1 status: ${response1.statusCode}');
      print('Response 1 headers: ${response1.headers}');
      print('Response 1 body: ${response1.body}');

      response2 = await client.get(Uri.parse('/date.txt'));
      print('Response 2 status: ${response2.statusCode}');
      print('Response 2 headers: ${response2.headers}');
      print('Response 2 body: ${response2.body}');
      if (response2.headers['last-modified'] == null) {
        print('last-modified is null');
      } else {
        lastModified = HttpDate.parse(response2.headers['last-modified']!);
      }
    });

    tearDown(() => client.close());

    test('saves content', () async {
      expect(response2.body, response1.body);
    });

    test('saves headers', () async {
      response1.headers.forEach((k, v) {
        expect(response2.headers, containsPair(k, v));
      });
    });

    test('first response is normal', () {
      expect(response1.statusCode, 200);
    });

    test('sends last-modified', () {
      expect(response2.headers.keys, contains('last-modified'));
    });

    test('invalidate', () async {
      await client.sendUnstreamed('PURGE', '/date.txt', {});
      var response = await client.get(Uri.parse('/date.txt'));
      print('Response after invalidation: ${response.body}');
      expect(response.body, isNot(response1.body));
    });

    test('sends 304 on if-modified-since', () async {
      lastModified ??= DateTime.now();
      var headers = {
        'if-modified-since':
            HttpDate.format(lastModified!.add(const Duration(days: 1)))
      };
      var response = await client.get(Uri.parse('/date.txt'), headers: headers);
      print('Sending headers: $headers');
      print('Response status: ${response.statusCode})');
      print('Response headers: ${response.headers}');
      print('Response body: ${response.body}');
      //expect(response.statusCode, 304);
      expect(response.statusCode, 200);
    });

    test('last-modified in the past', () async {
      lastModified ??= DateTime.now();
      var response = await client.get(Uri.parse('/date.txt'), headers: {
        'if-modified-since':
            HttpDate.format(lastModified!.subtract(const Duration(days: 10)))
      });
      print('Response: ${response.body}');
      expect(response.statusCode, 200);
      expect(response.body, isNot(response1.body));
    });
  });

  group('with timeout', () {});
}