import 'dart:convert';

import 'package:http/http.dart' as http;

import '../../../model/account_data.dart';
import '../../http_errors.dart';
import '../nextcloud_ocs.dart';

/// Exchanges the user's real Nextcloud password for a scoped app password via
/// `GET /ocs/v2.php/core/getapppassword`. All subsequent Nextcloud calls then
/// authenticate with the app password (see [AccountData.getBasicAuthHeader]),
/// which is what the push-v2 registration binds to.
///
/// Must authenticate with the *real* password — an app password cannot mint
/// another one.
class GetAppPassword {
  final http.Client _client;

  GetAppPassword({http.Client? client}) : _client = client ?? http.Client();

  /// Returns the freshly minted app password. Throws on any transport or
  /// protocol error — a 401 becomes an [AuthException], which the login flow
  /// reads as "Nextcloud rejects the password" (two-factor authentication or
  /// password mismatch) and answers with the interactive Login Flow v2.
  Future<String> run() async {
    const label = 'Nextcloud getapppassword';
    final response = (await sendGuarded(
      label,
      () => _client.get(
        NextcloudOcs.uri('core/getapppassword'),
        headers: {
          ...NextcloudOcs.headers(),
          // Deliberately NOT the shared Authorization value: that one prefers
          // the app password, but an app password cannot mint another one —
          // this endpoint requires the real password.
          'Authorization': AccountData().getRealPasswordBasicAuthHeader(),
        },
      ),
    ))!;
    if (response.statusCode < 200 || response.statusCode >= 300) {
      throwForStatus(
        response.statusCode,
        httpErrorDetail(label, response.body, response.statusCode),
      );
    }
    final json = jsonDecode(utf8.decode(response.bodyBytes));
    final data = (json as Map)['ocs']?['data'];
    final appPassword = data is Map ? data['apppassword'] as String? : null;
    if (appPassword == null || appPassword.isEmpty) {
      throw Exception('getapppassword: no apppassword in response');
    }
    return appPassword;
  }
}
