import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../../core/config/app_environment_provider.dart';
import '../../../core/network/api_error.dart';
import '../../../core/network/http_client_provider.dart';
import '../domain/auth_models.dart';
import 'auth_api_client.dart';
import 'auth_session_store.dart';

abstract class AuthRepository {
  const AuthRepository();

  Future<PhoneLoginLinkInitResponse> requestPhoneLoginLink(String phoneNumber);

  Future<PhoneLoginLinkPollResponse> pollPhoneLoginLink(String pendingRef);

  Future<PhoneLoginLinkInitResponse?> loadPendingPhoneLoginLink();

  Future<void> clearPendingPhoneLoginLink();

  Future<StoredAuthSession?> loadSession();

  Future<void> logout();
}

abstract class LegacyPhoneLoginRepository {
  const LegacyPhoneLoginRepository();

  Future<PhoneLoginResponse> phoneLogin(String phoneNumber);
}

class RemoteAuthRepository
    implements AuthRepository, LegacyPhoneLoginRepository {
  const RemoteAuthRepository({
    required this.apiClient,
    required this.sessionStore,
    required this.appVersion,
  });

  final AuthApiClient apiClient;
  final AuthSessionStore sessionStore;
  final String appVersion;

  @override
  Future<PhoneLoginResponse> phoneLogin(String phoneNumber) async {
    final normalizedPhone = _normalizePhoneNumber(phoneNumber);
    if (normalizedPhone.length < 9) {
      throw _invalidPhoneNumber();
    }

    final response = await apiClient.phoneLogin(
      PhoneLoginRequest(
        phoneNumber: normalizedPhone,
        device: LoginDeviceInfo(
          platform: _platformName(),
          appVersion: appVersion,
          deviceName: _deviceName(),
        ),
      ),
    );
    await sessionStore.save(response);
    return response;
  }

  @override
  Future<PhoneLoginLinkInitResponse> requestPhoneLoginLink(
    String phoneNumber,
  ) async {
    final normalizedPhone = _normalizePhoneNumber(phoneNumber);
    if (normalizedPhone.length < 9) {
      throw _invalidPhoneNumber();
    }

    final response = await apiClient.requestPhoneLoginLink(
      PhoneLoginLinkInitRequest(
        phoneNumber: normalizedPhone,
        device: LoginDeviceInfo(
          platform: _platformName(),
          appVersion: appVersion,
          deviceName: _deviceName(),
        ),
      ),
    );
    await sessionStore.savePendingLink(response);
    return response;
  }

  @override
  Future<PhoneLoginLinkPollResponse> pollPhoneLoginLink(
    String pendingRef,
  ) async {
    final response = await apiClient.pollPhoneLoginLink(
      PhoneLoginLinkPollRequest(pendingRef: pendingRef),
    );
    final session = response.session;
    debugPrint(
      'RemoteAuthRepository.pollPhoneLoginLink status=${response.status} hasSession=${session != null} token=${session?.token ?? ''} expiresAt=${session?.expiresAt.toUtc().toIso8601String() ?? ''}',
    );
    if (session != null) {
      await sessionStore.save(session);
      await sessionStore.clearPendingLink();
    } else if (response.isExpired) {
      await sessionStore.clearPendingLink();
    }
    return response;
  }

  @override
  Future<PhoneLoginLinkInitResponse?> loadPendingPhoneLoginLink() {
    return sessionStore.loadPendingLink();
  }

  @override
  Future<void> clearPendingPhoneLoginLink() {
    return sessionStore.clearPendingLink();
  }

  @override
  Future<StoredAuthSession?> loadSession() => sessionStore.load();

  @override
  Future<void> logout() => sessionStore.clear();

  String _normalizePhoneNumber(String value) {
    return value.replaceAll(RegExp(r'[^0-9+]'), '');
  }

  ApiException _invalidPhoneNumber() {
    return const ApiException(
      statusCode: 400,
      apiError: ApiError(
        error: '전화번호 형식을 확인해 주세요.',
        code: 'invalid_phone_number',
        details: {},
      ),
    );
  }

  String _platformName() {
    if (kIsWeb) {
      return 'web';
    }
    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        return 'android';
      case TargetPlatform.iOS:
        return 'ios';
      case TargetPlatform.macOS:
        return 'macos';
      case TargetPlatform.windows:
        return 'windows';
      case TargetPlatform.linux:
        return 'linux';
      case TargetPlatform.fuchsia:
        return 'fuchsia';
    }
  }

  String _deviceName() {
    if (kIsWeb) {
      return 'flutter-web';
    }
    return _platformName();
  }
}

final authApiClientProvider = Provider<AuthApiClient>((ref) {
  final environment = ref.watch(appEnvironmentProvider);
  return AuthApiClient(
    httpClient: ref.watch(httpClientProvider),
    baseUri: Uri.parse(environment.authApiBaseUrl),
  );
});

final authRepositoryProvider = Provider<AuthRepository>((ref) {
  final environment = ref.watch(appEnvironmentProvider);
  return RemoteAuthRepository(
    apiClient: ref.watch(authApiClientProvider),
    sessionStore: ref.watch(authSessionStoreProvider),
    appVersion: environment.appVersion,
  );
});

final legacyPhoneLoginRepositoryProvider = Provider<LegacyPhoneLoginRepository>(
  (ref) {
    final environment = ref.watch(appEnvironmentProvider);
    return RemoteAuthRepository(
      apiClient: ref.watch(authApiClientProvider),
      sessionStore: ref.watch(authSessionStoreProvider),
      appVersion: environment.appVersion,
    );
  },
);
