import 'dart:async';
import 'dart:io';

import '../../../shared/utils/async_lock.dart';
import '../../../shared/utils/url_utils.dart';
import '../../contracts/tmdb.dart';
import 'json_file_cache_io.dart';

class DiscoverPageCacheScope {
  final String apiBaseUrl;
  final String imageBaseUrl;
  final String language;

  const DiscoverPageCacheScope({
    required this.apiBaseUrl,
    required this.imageBaseUrl,
    required this.language,
  });

  String get cacheKey => [
    _normalize(apiBaseUrl),
    _normalize(imageBaseUrl),
    language,
  ].map(sanitizeCacheKeyComponent).join('_');

  static String _normalize(String value) => stripTrailingSlash(value.trim());
}

class DiscoverPageCacheEntry {
  final TmdbRecommendationsRes data;
  final DateTime savedAt;

  const DiscoverPageCacheEntry({required this.data, required this.savedAt});

  factory DiscoverPageCacheEntry.fromJson(Map<String, dynamic> json) {
    return DiscoverPageCacheEntry(
      data: TmdbRecommendationsRes.fromJson(
        json['data'] as Map<String, dynamic>,
      ),
      savedAt: DateTime.parse(json['savedAt'] as String),
    );
  }

  Map<String, dynamic> toJson() => {
    'data': data.toJson(),
    'savedAt': savedAt.toUtc().toIso8601String(),
  };
}

class DiscoverPageCacheStore {
  final Directory _directory;
  final Map<String, AsyncLock> _locks = {};

  DiscoverPageCacheStore({required this._directory});

  Future<DiscoverPageCacheEntry?> readRow(
    DiscoverPageCacheScope scope,
    String rowKey,
  ) async {
    return readJsonCacheFile(
      _rowFile(scope, rowKey),
      DiscoverPageCacheEntry.fromJson,
    );
  }

  Future<void> writeRow(
    DiscoverPageCacheScope scope,
    String rowKey,
    TmdbRecommendationsRes data,
  ) async {
    final lock = _locks.putIfAbsent(_lockKey(scope, rowKey), AsyncLock.new);
    await lock.run(() async {
      await writeJsonCacheFile(
        _rowFile(scope, rowKey),
        DiscoverPageCacheEntry(
          data: data,
          savedAt: DateTime.now().toUtc(),
        ).toJson(),
      );
    });
  }

  File _rowFile(DiscoverPageCacheScope scope, String rowKey) {
    final safeRowKey = sanitizeCacheKeyComponent(rowKey);
    return File(
      '${_directory.path}/discover_page_cache_${scope.cacheKey}_$safeRowKey.json',
    );
  }

  String _lockKey(DiscoverPageCacheScope scope, String rowKey) =>
      '${scope.cacheKey}:$rowKey';
}
