import 'package:gametime_server/api/router.dart';
import 'package:gametime_server/api/share_api.dart';
import 'package:gametime_server/application/application.dart';
import 'package:gametime_server/domain/domain.dart';
import 'package:shelf/shelf.dart';
import 'package:test/test.dart';

void main() {
  test('share routes require bearer authentication', () async {
    final handler = buildApiHandler(shareApi: _shareApi());
    final requests = [
      Request('POST', Uri.parse('http://localhost/shares')),
      Request('GET', Uri.parse('http://localhost/shares/inbox')),
      Request('POST', Uri.parse('http://localhost/shares/share-1/accept')),
      Request('POST', Uri.parse('http://localhost/shares/share-1/decline')),
      Request('POST', Uri.parse('http://localhost/shares/share-1/revoke')),
    ];

    for (final request in requests) {
      final response = await handler(request);

      expect(response.statusCode, 401, reason: request.url.path);
    }
  });
}

ShareApi _shareApi() {
  final clock = _FakeClock(DateTime.utc(2026, 7, 19, 12));
  final ids = _FakeIds();
  final users = _FakeUserRepository(
    UserAccount(
      id: 'user-1',
      email: 'user@example.com',
      passwordHash: 'hash',
      createdAt: clock.now(),
      updatedAt: clock.now(),
    ),
  );
  final sessions = _FakeAuthSessionRepository(
    AuthSession(
      id: 'session-1',
      userId: 'user-1',
      tokenHash: 'token-hash:valid-token',
      issuedAt: clock.now(),
      expiresAt: clock.now().add(const Duration(days: 1)),
    ),
  );
  final tokens = _FakeTokenService();
  final authenticate = AuthenticateRequestUseCase(
    users: users,
    sessions: sessions,
    tokens: tokens,
    clock: clock,
  );
  final shares = _FakeShareRepository();
  final resources = _FakeSyncedResourceRepository();
  return ShareApi(
    createShare: CreateShareUseCase(
      users: users,
      shares: shares,
      clock: clock,
      ids: ids,
    ),
    listInbox: ListInboxUseCase(shares: shares),
    acceptShare: AcceptShareUseCase(
      shares: shares,
      resources: resources,
      clock: clock,
      ids: ids,
    ),
    declineShare: DeclineShareUseCase(shares: shares, clock: clock),
    revokeShare: RevokeShareUseCase(shares: shares, clock: clock),
    authenticateRequest: authenticate,
  );
}

final class _FakeUserRepository implements UserRepository {
  const _FakeUserRepository(this.user);

  final UserAccount user;

  @override
  Future<UserAccount?> findByEmail(String email) async {
    return user.email == email.toLowerCase() ? user : null;
  }

  @override
  Future<UserAccount?> findById(String id) async {
    return id == user.id ? user : null;
  }

  @override
  Future<void> insert(UserAccount user) async {}

  @override
  Future<void> updatePasswordHash({
    required String userId,
    required String passwordHash,
    required DateTime updatedAt,
  }) async {}
}

final class _FakeAuthSessionRepository implements AuthSessionRepository {
  const _FakeAuthSessionRepository(this.session);

  final AuthSession session;

  @override
  Future<AuthSession?> findByTokenHash(String tokenHash) async {
    return tokenHash == session.tokenHash ? session : null;
  }

  @override
  Future<void> insert(AuthSession session) async {}

  @override
  Future<void> revoke({
    required String sessionId,
    required DateTime revokedAt,
  }) async {}
}

final class _FakeShareRepository implements ShareRepository {
  @override
  Future<void> insertShare({
    required Share share,
    required List<ShareRecipient> recipients,
  }) async {}

  @override
  Future<List<ShareInboxItem>> listInbox(String recipientUserId) async {
    return const [];
  }

  @override
  Future<Share?> findShareById(String shareId) async {
    return null;
  }

  @override
  Future<ShareRecipient?> findRecipient({
    required String shareId,
    required String recipientUserId,
  }) async {
    return null;
  }

  @override
  Future<void> updateRecipientStatus({
    required String recipientId,
    required ShareRecipientStatus status,
    required DateTime respondedAt,
  }) async {}

  @override
  Future<void> revokeShare({
    required String shareId,
    required DateTime revokedAt,
  }) async {}
}

final class _FakeSyncedResourceRepository implements SyncedResourceRepository {
  @override
  Future<SyncWriteResult> upsertWithLww(SyncedResource resource) async {
    return SyncWriteResult(
      status: SyncWriteStatus.accepted,
      resource: resource,
    );
  }

  @override
  Future<List<SyncedResource>> findAllForUserSince({
    required String ownerUserId,
    DateTime? since,
  }) async {
    return const [];
  }
}

final class _FakeTokenService implements OpaqueTokenService {
  @override
  String generateToken() => 'valid-token';

  @override
  String hashToken(String token) => 'token-hash:$token';
}

final class _FakeClock implements Clock {
  const _FakeClock(this.value);

  final DateTime value;

  @override
  DateTime now() => value;
}

final class _FakeIds implements IdGenerator {
  var _next = 0;

  @override
  String newId() {
    _next += 1;
    return 'id-$_next';
  }
}
