import 'dart:io';

import 'package:gametime_server/api/auth_api.dart';
import 'package:gametime_server/api/router.dart';
import 'package:gametime_server/api/share_api.dart';
import 'package:gametime_server/api/sync_api.dart';
import 'package:gametime_server/application/application.dart';
import 'package:gametime_server/infrastructure/postgres/postgres.dart';
import 'package:gametime_server/infrastructure/security/security.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

Future<void> main(List<String> arguments) async {
  final port = int.tryParse(Platform.environment['PORT'] ?? '') ?? 8080;
  final connection = await PostgresConnectionFactory(
    PostgresConnectionConfig.fromEnvironment(),
  ).open();

  final users = PostgresUserRepository(connection);
  final sessions = PostgresAuthSessionRepository(connection);
  final resources = PostgresSyncedResourceRepository(connection);
  final shares = PostgresShareRepository(connection);
  final passwordHasher = Pbkdf2PasswordHasher();
  final tokens = SecureOpaqueTokenService();
  const clock = SystemClock();
  final ids = UuidV4Generator();
  final authenticateRequest = AuthenticateRequestUseCase(
    users: users,
    sessions: sessions,
    tokens: tokens,
    clock: clock,
  );
  final authApi = AuthApi(
    registerUser: RegisterUserUseCase(
      users: users,
      passwordHasher: passwordHasher,
      clock: clock,
      ids: ids,
    ),
    login: LoginUseCase(
      users: users,
      sessions: sessions,
      passwordHasher: passwordHasher,
      tokens: tokens,
      clock: clock,
      ids: ids,
    ),
    logout: LogoutUseCase(sessions: sessions, tokens: tokens, clock: clock),
    authenticateRequest: authenticateRequest,
  );
  final pushSync = PushSyncUseCase(
    resources: resources,
    clock: clock,
    ids: ids,
  );
  final pullSync = PullSyncUseCase(resources: resources, clock: clock);
  final syncApi = SyncApi(
    pushSync: pushSync,
    pullSync: pullSync,
    exchangeSync: ExchangeSyncUseCase(push: pushSync, pull: pullSync),
    authenticateRequest: authenticateRequest,
  );
  final shareApi = 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: authenticateRequest,
  );

  final server = await shelf_io.serve(
    buildApiHandler(authApi: authApi, syncApi: syncApi, shareApi: shareApi),
    InternetAddress.anyIPv4,
    port,
  );

  print(
    'GameTime server listening on ${server.address.address}:${server.port}',
  );
}
