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

import '../../../shared/utils/async_lock.dart';
import '../../../shared/utils/url_utils.dart';
import '../../contracts/server.dart';
import '../../domain/ports/server_repository.dart';

class JsonServerRepository implements ServerRepository {
  final File _file;
  final _lock = AsyncLock();

  JsonServerRepository({required this._file});

  Future<List<EmbyServer>> _read() async {
    if (!await _file.exists()) return <EmbyServer>[];
    final raw = await _file.readAsString();
    if (raw.trim().isEmpty) return <EmbyServer>[];
    try {
      final list = jsonDecode(raw) as List<dynamic>;
      return list
          .map((e) => EmbyServer.fromJson(e as Map<String, dynamic>))
          .toList();
    } catch (_) {
      return <EmbyServer>[];
    }
  }

  Future<void> _write(List<EmbyServer> servers) async {
    if (!await _file.parent.exists()) {
      await _file.parent.create(recursive: true);
    }
    final json = servers.map((e) => e.toJson()).toList();
    await _file.writeAsString(
      const JsonEncoder.withIndent('  ').convert(json),
      flush: true,
    );
  }

  @override
  Future<List<EmbyServer>> list() => _lock.run(_read);

  @override
  Future<EmbyServer?> findById(String id) async {
    final servers = await list();
    for (final s in servers) {
      if (s.id == id) return s;
    }
    return null;
  }

  @override
  Future<EmbyServer?> findByBaseUrl(String baseUrl) async {
    final servers = await list();
    final normalized = stripTrailingSlash(baseUrl.toLowerCase());
    for (final s in servers) {
      if (stripTrailingSlash(s.baseUrl.toLowerCase()) == normalized) {
        return s;
      }
    }
    return null;
  }

  @override
  Future<EmbyServer> create(EmbyServer server) async {
    return _lock.run(() async {
      final list = await _read();
      list.add(server);
      await _write(list);
      return server;
    });
  }

  @override
  Future<EmbyServer> update(EmbyServer server) async {
    return _lock.run(() async {
      final list = await _read();
      final idx = list.indexWhere((e) => e.id == server.id);
      if (idx < 0) {
        list.add(server);
      } else {
        list[idx] = server;
      }
      await _write(list);
      return server;
    });
  }

  @override
  Future<void> deleteById(String id) async {
    await _lock.run(() async {
      final list = await _read();
      list.removeWhere((e) => e.id == id);
      await _write(list);
    });
  }

  @override
  Future<void> replaceAll(List<EmbyServer> servers) async {
    await _lock.run(() async {
      await _write(List<EmbyServer>.from(servers));
    });
  }

  @override
  Future<void> touchConnectedAt(String id, String updatedAt) async {
    await _lock.run(() async {
      final list = await _read();
      final idx = list.indexWhere((e) => e.id == id);
      if (idx < 0) return;
      list[idx] = list[idx].copyWith(
        lastConnectedAt: updatedAt,
        updatedAt: updatedAt,
      );
      await _write(list);
    });
  }
}
