import 'package:logging/logging.dart';

import 'package:pshared/api/errors/authorization_failed.dart';
import 'package:pshared/api/requests/login.dart';
import 'package:pshared/api/requests/login_data.dart';
import 'package:pshared/api/responses/account.dart';
import 'package:pshared/api/responses/login.dart';
import 'package:pshared/api/responses/login_pending.dart';
import 'package:pshared/config/constants.dart';
import 'package:pshared/data/mapper/account/account.dart';
import 'package:pshared/models/account/account.dart';
import 'package:pshared/models/auth/login_outcome.dart';
import 'package:pshared/models/auth/pending_login.dart';
import 'package:pshared/models/session_identifier.dart';
import 'package:pshared/service/authorization/circuit_breaker.dart';
import 'package:pshared/service/authorization/retry_helper.dart';
import 'package:pshared/service/authorization/storage.dart';
import 'package:pshared/service/authorization/token.dart';
import 'package:pshared/service/device_id.dart';
import 'package:pshared/utils/exception.dart';
import 'package:pshared/utils/http/requests.dart' as httpr;


/// AuthorizationService provides centralized authorization management
/// with token refresh, retry logic, and circuit breaker patterns
class AuthorizationService {
  static final _logger = Logger('service.authorization.auth_service');

  static Future<LoginOutcome> login(String service, LoginData login) async {
    _logger.fine('Logging in ${login.login} with ${login.locale} locale');
    final deviceId = await DeviceIdManager.getDeviceId();
    final response = await httpr.getPOSTResponse(
      service,
      '/login',
      LoginRequest(
        login: login,
        deviceId: deviceId,
        clientId: Constants.clientId,
      ).toJson(),
    );

    if (response.containsKey('refreshToken')) {
      return LoginOutcome.completed(
        (await completeLogin(response)).account.toDomain(),
      );
    }
    if (response.containsKey('pendingToken')) {
      final pending = PendingLogin.fromResponse(
        PendingLoginResponse.fromJson(response),
        session: SessionIdentifier(
          clientId: Constants.clientId,
          deviceId: deviceId,
        ),
      );
      return LoginOutcome.pending(pending);
    }
    throw AuthenticationFailedException(
      'Unexpected login response',
      Exception(response.toString()),
    );
  }

  static Future<void> _updateAccessToken(AccountResponse response) async {
    await AuthorizationStorage.updateToken(response.accessToken);
  }

  static Future<void> _updateTokens(LoginResponse response) async {
    await _updateAccessToken(response);
    return AuthorizationStorage.updateRefreshToken(response.refreshToken);
  }

  static Future<LoginResponse> _completeLogin(
    Map<String, dynamic> response,
  ) async {
    final LoginResponse lr = LoginResponse.fromJson(response);
    await _updateTokens(lr);
    return lr;
  }

  static Future<LoginResponse> completeLogin(Map<String, dynamic> response) =>
      _completeLogin(response);

  static Future<Account> restore() async {
    return (await TokenService.refreshAccessToken()).account.toDomain();
  }

  static Future<void> logout() async {
    return AuthorizationStorage.removeTokens();
  }

  // Original AuthorizationService methods - keeping the interface unchanged
  static Future<Map<String, dynamic>> getGETResponse(
    String service,
    String url,
  ) async {
    final token = await TokenService.getAccessTokenSafe();
    return httpr.getGETResponse(service, url, authToken: token);
  }

  static Future<httpr.BinaryResponse> getGETBinaryResponse(
    String service,
    String url,
  ) async {
    final token = await TokenService.getAccessTokenSafe();
    return httpr.getBinaryGETResponse(service, url, authToken: token);
  }

  static Future<Map<String, dynamic>> getPOSTResponse(
    String service,
    String url,
    Map<String, dynamic> body,
  ) async {
    final token = await TokenService.getAccessTokenSafe();
    return httpr.getPOSTResponse(service, url, body, authToken: token);
  }

  static Future<Map<String, dynamic>> getPUTResponse(
    String service,
    String url,
    Map<String, dynamic> body,
  ) async {
    final token = await TokenService.getAccessTokenSafe();
    return httpr.getPUTResponse(service, url, body, authToken: token);
  }

  static Future<Map<String, dynamic>> getPATCHResponse(
    String service,
    String url,
    Map<String, dynamic> body,
  ) async {
    final token = await TokenService.getAccessTokenSafe();
    return httpr.getPATCHResponse(service, url, body, authToken: token);
  }

  static Future<Map<String, dynamic>> getDELETEResponse(
    String service,
    String url,
    Map<String, dynamic> body,
  ) async {
    final token = await TokenService.getAccessTokenSafe();
    return httpr.getDELETEResponse(service, url, body, authToken: token);
  }

  static Future<String> getFileUploadResponseAuth(
    String service,
    String url,
    String fileName,
    String fileType,
    String mediaType,
    List<int> bytes,
  ) async {
    final token = await TokenService.getAccessTokenSafe();
    final res = await httpr.getFileUploadResponse(
      service,
      url,
      fileName,
      fileType,
      mediaType,
      bytes,
      authToken: token,
    );
    if (res == null) {
      throw Exception('Upload failed');
    }
    return res.url;
  }

  static Future<bool> isAuthorizationStored() async =>
      AuthorizationStorage.isAuthorizationStored();

  /// Execute an operation with automatic token management and retry logic
  static Future<T> executeWithAuth<T>(
    Future<T> Function() operation,
    String description, {
    int? maxRetries,
  }) async => AuthCircuitBreaker.execute(
    () async => RetryHelper.withExponentialBackoff(
      operation,
      maxRetries: maxRetries ?? 3,
      initialDelay: Duration(milliseconds: 100),
      maxDelay: Duration(seconds: 5),
      shouldRetry: (error) => RetryHelper.isRetryableError(error),
    ),
  );

  /// Handle 401 unauthorized errors with automatic token recovery
  static Future<T> handleUnauthorized<T>(
    Future<T> Function() operation,
    String description,
  ) async {
    _logger.warning(
      'Handling unauthorized error with token recovery: $description',
    );

    return executeWithAuth(() async {
      try {
        // Attempt token recovery first
        await TokenService.handleUnauthorized();

        // Retry the original operation
        return await operation();
      } catch (e) {
        _logger.severe('Token recovery failed', e);
        throw AuthenticationFailedException(
          'Token recovery failed',
          toException(e),
        );
      }
    }, 'unauthorized recovery: $description');
  }
}
