import 'package:logging/logging.dart';

import 'package:pshared/service/authorization/service.dart';
import 'package:pshared/utils/http/params.dart';


class BasicService<T> {
  final String _objectType;
  final Logger _logger;
  final List<T> Function(Map<String, dynamic> json) fromJson;

  Logger get logger => _logger;

  BasicService({
    required String objectType,
    required this.fromJson,
  }) : _objectType = objectType, _logger = Logger('service.$objectType');

  String _refLog(String ref) => ref.isEmpty ? '<not set>' : ref;

  Future<List<T>> list(String organizationRef, String parentRef, {int? limit, int? offset, bool? fetchArchived}) async {
    _logger.fine('Loading all for organization ${_refLog(organizationRef)} and parent ${_refLog(organizationRef)} with: limit=${_formatParameter(limit)}, offset=${_formatParameter(offset)}, fetchArchived=${_formatParameter(fetchArchived)}...');
    
    return _getObjects(
      AuthorizationService.getGETResponse(
        _objectType, 
        paramsToUriString(
          path: '/list/$organizationRef/$parentRef',
          limit: limit,
          offset: offset,
          fetchArchived: fetchArchived,
        ),
      ),
    );
  }

  Future<T> get(String objectRef) async {
    _logger.fine('Loading $_objectType $objectRef');
    final objects = await _getObjects(
      AuthorizationService.getGETResponse(_objectType, '/$objectRef'),
    );
    return objects.first;
  }

  Future<List<T>> create(String organizationRef, Map<String, dynamic> request) async {
    _logger.fine('Creating new...');
    return _getObjects(
      AuthorizationService.getPOSTResponse(_objectType, '/$organizationRef', request),
    );
  }

  Future<List<T>> update(Map<String, dynamic> request) async {
    _logger.fine('Patching...');
    return _getObjects(
      AuthorizationService.getPUTResponse(_objectType, '/', request,
      ),
    );
  }

  Future<List<T>> delete(String objecRef, {Map<String, dynamic>? request}) async {
    _logger.fine('Deleting $_objectType $objecRef');
    return _getObjects(
      AuthorizationService.getDELETEResponse(_objectType, '/$objecRef', request ?? {}),
    );
  }

  Future<List<T>> archive({
    required String organizationRef, 
    required String objectRef, 
    required bool newIsArchived, 
    bool? cascade,
  }) async {
    _logger.fine('Setting new archive status $newIsArchived to $objectRef');
    return _getObjects(
      AuthorizationService.getGETResponse(_objectType, '/archive/$organizationRef/$objectRef?archived=$newIsArchived&cascade=${cascade ?? false}'),
    );
  }

  Future<List<T>> _getObjects(Future<Map<String, dynamic>> future) async {
    try {
      final responseJson = await future;
      final objects = fromJson(responseJson);
      _logger.fine('Fetched ${objects.length} object(s)');
      return objects;
    } catch (e, stackTrace) {
      _logger.severe('Failed to fetch', e, stackTrace);
      rethrow;
    }
  }

  String _formatParameter(dynamic value) {
    return value?.toString() ?? '<not specified>';
  }
}
