// HTTP client for the optional self-hosted BadNote Server (/api/v1).
// WebDAV remains the primary NAS sync path; this client covers health, auth,
// vault manifest assist, and OCR job submit/poll.

import 'dart:convert';

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

class BadNoteServerConfig {
  const BadNoteServerConfig({
    this.baseUrl = '',
    this.username = '',
    this.password = '',
    this.token = '',
    this.userId = '',
  });

  final String baseUrl;
  final String username;
  final String password;
  final String token;
  final String userId;

  bool get isConfigured => baseUrl.trim().isNotEmpty;
  bool get isLoggedIn => token.isNotEmpty;

  Uri? uri(String path) {
    final base = baseUrl.trim().replaceAll(RegExp(r'/+$'), '');
    if (base.isEmpty) return null;
    final p = path.startsWith('/') ? path : '/$path';
    return Uri.parse('$base$p');
  }

  BadNoteServerConfig copyWith({
    String? baseUrl,
    String? username,
    String? password,
    String? token,
    String? userId,
  }) =>
      BadNoteServerConfig(
        baseUrl: baseUrl ?? this.baseUrl,
        username: username ?? this.username,
        password: password ?? this.password,
        token: token ?? this.token,
        userId: userId ?? this.userId,
      );

  static const _kBase = 'badnote.server.baseUrl';
  static const _kUser = 'badnote.server.username';
  static const _kPass = 'badnote.server.password';
  static const _kToken = 'badnote.server.token';
  static const _kUid = 'badnote.server.userId';

  static Future<BadNoteServerConfig> load(SharedPreferences prefs) async {
    return BadNoteServerConfig(
      baseUrl: prefs.getString(_kBase) ?? '',
      username: prefs.getString(_kUser) ?? '',
      password: prefs.getString(_kPass) ?? '',
      token: prefs.getString(_kToken) ?? '',
      userId: prefs.getString(_kUid) ?? '',
    );
  }

  Future<void> save(SharedPreferences prefs) async {
    await prefs.setString(_kBase, baseUrl);
    await prefs.setString(_kUser, username);
    await prefs.setString(_kPass, password);
    await prefs.setString(_kToken, token);
    await prefs.setString(_kUid, userId);
  }
}

class BadNoteServerClient {
  BadNoteServerClient(this.config, {http.Client? httpClient})
      : _http = httpClient ?? http.Client();

  BadNoteServerConfig config;
  final http.Client _http;

  Map<String, String> get _authHeaders => {
        if (config.token.isNotEmpty) 'Authorization': 'Bearer ${config.token}',
      };

  Future<Map<String, dynamic>> health() async {
    final uri = config.uri('/api/v1/health');
    if (uri == null) throw StateError('server URL not set');
    final res = await _http.get(uri).timeout(const Duration(seconds: 8));
    if (res.statusCode != 200) {
      throw StateError('health ${res.statusCode}: ${res.body}');
    }
    return jsonDecode(res.body) as Map<String, dynamic>;
  }

  Future<BadNoteServerConfig> registerOrLogin({
    required String username,
    required String password,
  }) async {
    final registerUri = config.uri('/api/v1/auth/register');
    if (registerUri == null) throw StateError('server URL not set');
    var res = await _http.post(
      registerUri,
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({'username': username, 'password': password}),
    );
    if (res.statusCode == 409) {
      final loginUri = config.uri('/api/v1/auth/login')!;
      res = await _http.post(
        loginUri,
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({'username': username, 'password': password}),
      );
    }
    if (res.statusCode != 200 && res.statusCode != 201) {
      throw StateError('auth ${res.statusCode}: ${res.body}');
    }
    final body = jsonDecode(res.body) as Map<String, dynamic>;
    config = config.copyWith(
      username: username,
      password: password,
      token: body['token'] as String? ?? '',
      userId: body['user_id'] as String? ?? '',
    );
    return config;
  }

  Future<Map<String, dynamic>> vaultManifest() async {
    final uri = config.uri('/api/v1/vault/manifest');
    if (uri == null) throw StateError('server URL not set');
    final res = await _http.get(uri, headers: _authHeaders);
    if (res.statusCode != 200) {
      throw StateError('manifest ${res.statusCode}: ${res.body}');
    }
    return jsonDecode(res.body) as Map<String, dynamic>;
  }

  void close() => _http.close();
}
