import 'dart:async';
import 'dart:convert';

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

import '../../../core/config/app_environment_provider.dart';
import '../../../core/network/api_error.dart';
import '../../../core/network/http_client_provider.dart';
import '../../auth/data/auth_session_store.dart';
import '../domain/employee.dart';

class DirectoryApiClient {
  const DirectoryApiClient({
    required this.httpClient,
    required this.baseUri,
    required this.sessionStore,
    this.timeout = const Duration(seconds: 10),
  });

  final http.Client httpClient;
  final Uri baseUri;
  final AuthSessionStore sessionStore;
  final Duration timeout;

  Future<EmployeeListResponse> listEmployees({
    String? query,
    String? tenantId,
    String? tenantSlug,
    String? department,
    int limit = 50,
    int offset = 0,
    String? cursor,
  }) async {
    final response = await httpClient
        .get(
          _resolve(
            '/api/v1/tdc114plus/directory/employees',
            queryParameters: {
              'q': query,
              'tenantId': tenantId,
              'tenantSlug': tenantSlug,
              'department': department,
              'limit': '$limit',
              'offset': '$offset',
              'cursor': cursor,
            },
          ),
          headers: await _headers(),
        )
        .timeout(timeout);
    return EmployeeListResponse.fromJson(_decodeOk(response));
  }

  Future<EmployeeDetail> getEmployee(String employeeId) async {
    final response = await httpClient
        .get(
          _resolve('/api/v1/tdc114plus/directory/employees/$employeeId'),
          headers: await _headers(),
        )
        .timeout(timeout);
    return EmployeeDetail.fromJson(_decodeOk(response));
  }

  Future<Map<String, String>> _headers() async {
    final session = await sessionStore.load();
    return {
      'accept': 'application/json',
      if (session?.token != null) 'authorization': 'Bearer ${session!.token}',
    };
  }

  Map<String, dynamic> _decodeOk(http.Response response) {
    final decoded = _decodeObject(response.body);
    if (response.statusCode < 200 || response.statusCode >= 300) {
      throw ApiException(
        statusCode: response.statusCode,
        apiError: ApiError.fromJson(decoded),
      );
    }
    return decoded;
  }

  Uri _resolve(String path, {Map<String, String?> queryParameters = const {}}) {
    final normalizedBase = baseUri.path.endsWith('/')
        ? baseUri
        : baseUri.replace(path: '${baseUri.path}/');
    final uri = normalizedBase.resolve(path.replaceFirst(RegExp(r'^/'), ''));
    final filteredQuery = Map<String, String>.fromEntries(
      queryParameters.entries
          .where((entry) {
            return entry.value != null && entry.value!.trim().isNotEmpty;
          })
          .map((entry) => MapEntry(entry.key, entry.value!)),
    );
    return uri.replace(queryParameters: filteredQuery);
  }

  Map<String, dynamic> _decodeObject(String body) {
    final decoded = jsonDecode(body);
    if (decoded is Map<String, dynamic>) {
      return decoded;
    }
    throw const FormatException('Expected JSON object response');
  }
}

final directoryApiClientProvider = Provider<DirectoryApiClient>((ref) {
  final environment = ref.watch(appEnvironmentProvider);
  return DirectoryApiClient(
    httpClient: ref.watch(httpClientProvider),
    baseUri: Uri.parse(environment.directoryApiBaseUrl),
    sessionStore: ref.watch(authSessionStoreProvider),
  );
});
