import 'package:logging/logging.dart';

import 'package:share_plus/share_plus.dart';

import 'package:pshared/api/errors/unauthorized.dart';
import 'package:pshared/api/responses/organization.dart';
import 'package:pshared/models/describable.dart';
import 'package:pshared/models/organization/organization.dart';
import 'package:pshared/data/mapper/organization.dart';
import 'package:pshared/service/authorization/service.dart';
import 'package:pshared/service/files.dart';
import 'package:pshared/service/services.dart';
import 'package:pshared/utils/http/requests.dart';


class OrganizationService {
  static final _logger = Logger('service.organization');
  static const String _objectType = Services.organization;

  static Future<List<Organization>> list() async {
    _logger.fine('Loading all organizations');
    return _getOrganizations(AuthorizationService.getGETResponse(_objectType, ''));
  }

  static Future<Organization> load(String organizationRef) async {
    _logger.fine('Loading organization $organizationRef');
    final orgs = await _getOrganizations(AuthorizationService.getGETResponse(_objectType, organizationRef));
    return orgs.first;
  }

  static Future<Organization> loadByInvitation(String invitationRef) async {
    _logger.fine('Loading organization by invitation ref $invitationRef');
    final orgs = await _getOrganizations(getGETResponse(_objectType, 'invitation/$invitationRef'));
    return orgs.first;
  }

  static Future<List<Organization>> update(Organization organization) async {
    _logger.fine('Patching organization ${organization.id}');
    // Convert domain object to DTO, then to JSON
    return _getOrganizations(
      AuthorizationService.getPUTResponse(_objectType, '', organization.toDTO().toJson())
    );
  }

  static Future<Organization> updateSettings(
    Organization organization, {
    String? name,
    String? description,
    String? timeZone,
    String? logoUrl,
  }) async {
    _logger.fine('Updating organization settings ${organization.id}');
    final updatedDescribable = (name != null || description != null)
      ? organization.describable.copyWith(
          name: name,
          description: description != null ? () => description : null,
        )
      : organization.describable;

    final updatedOrg = organization.copyWith(
      describable: updatedDescribable,
      timeZone: timeZone,
      logoUrl: logoUrl != null ? () => logoUrl : null,
    );

    final updated = await update(updatedOrg);
    return updated.firstWhere(
      (org) => org.id == organization.id,
      orElse: () => updated.first,
    );
  }

  static Future<Organization> uploadLogoAndUpdate(Organization organization, XFile logoFile) async {
    _logger.fine('Uploading logo for organization ${organization.id}');
    final url = await uploadLogo(organization.id, logoFile);
    return updateSettings(organization, logoUrl: url);
  }

  static Future<String> uploadLogo(String organizationRef, XFile logoFile) async {
    _logger.fine('Uploading logo');
    return FilesService.uploadImage(_objectType, organizationRef, logoFile);
  }

  static Future<List<Organization>> _getOrganizations(Future<Map<String, dynamic>> future) async {
    try {
      final responseJson = await future;
      final response = OrganizationResponse.fromJson(responseJson);
      final orgs = response.organizations.map((dto) => dto.toDomain()).toList();

      if (orgs.isEmpty) throw ErrorUnauthorized();
      _logger.fine('Fetched ${orgs.length} organization(s)');
      return orgs;
    } catch (e, stackTrace) {
      _logger.severe('Failed to fetch organizations', e, stackTrace);
      rethrow;
    }
  }
}
