import 'dart:async';

import 'package:flutter/material.dart';

import 'package:collection/collection.dart';

import 'package:share_plus/share_plus.dart';

import 'package:pshared/config/constants.dart';
import 'package:pshared/models/organization/organization.dart';
import 'package:pshared/models/auth/state.dart';
import 'package:pshared/provider/account.dart';
import 'package:pshared/provider/resource.dart';
import 'package:pshared/service/organization.dart';
import 'package:pshared/service/secure_storage.dart';
import 'package:pshared/utils/exception.dart';


class OrganizationsProvider extends ChangeNotifier {
  Resource<List<Organization>> _resource = Resource(data: []);
  Resource<List<Organization>> get resource => _resource;

  List<Organization> get organizations => _resource.data ?? [];
  String? _currentOrg;
  AccountProvider? _accountProvider;

  Organization get current => isOrganizationSet ? _current! : throw StateError('Organization is not set');

  Organization? _org(String? orgRef) => organizations.firstWhereOrNull((org) => org.id == orgRef);
  Organization? get _current => _org(_currentOrg);

  bool get isOrganizationSet => _current != null;
  bool get isLoading => _resource.isLoading;
  Object? get error => _resource.error;

  void updateAccount(AccountProvider accountProvider) {
    if (!identical(_accountProvider, accountProvider)) {
      _accountProvider = accountProvider;
    }
    _triggerLoadIfNeeded(accountProvider);
  }

  void _setResource(Resource<List<Organization>> newResource) {
    _resource = newResource;
    notifyListeners();
  }

  Future<List<Organization>> load() async {
    _setResource(_resource.copyWith(isLoading: true, error: null));
    try {
      final orgs = await OrganizationService.list();
      // keep fetched organizations so _org can resolve against them
      _setResource(Resource(data: orgs, isLoading: true));
      // fetch stored org
      String? org = await SecureStorageService.get(Constants.currentOrgKey);
      // check stored org availability
      org = orgs.firstWhereOrNull((o) => o.id == org)?.id;
      // fallback if org is not set or not available
      org ??= orgs.first.id;
      await setCurrentOrganization(org);
      _setResource(Resource(data: orgs, isLoading: false));
      return orgs;
    } catch (e) {
      _setResource(_resource.copyWith(isLoading: false, error: toException(e)));
      rethrow;
    }
  }

  void _triggerLoadIfNeeded(AccountProvider accountProvider) {
    if (accountProvider.authState != AuthState.ready) return;
    if (accountProvider.account == null) return;
    if (isLoading || isOrganizationSet) return;
    if (error != null) return;
    unawaited(load());
  }

  Future<Organization> loadByInvitation(String invitationRef) async {
    _setResource(_resource.copyWith(isLoading: true, error: null));
    try {
      final org = await OrganizationService.loadByInvitation(invitationRef);
      _setResource(Resource(data: [org], isLoading: true));
      await setCurrentOrganization(org.id);
      _setResource(Resource(data: [org], isLoading: false));
      return org;
    } catch (e) {
      _setResource(_resource.copyWith(isLoading: false, error: toException(e)));
      rethrow;
    }
  }

  bool _setCurrentOrganization(String? orgRef) {
    final organizationRef = _org(orgRef)?.id;
    if (organizationRef == null) return false;

    _currentOrg = organizationRef;
    return true;
  }

  Future<bool> setCurrentOrganization(String? orgRef) async {
    if (!_setCurrentOrganization(orgRef)) return false;
    await SecureStorageService.set(Constants.currentOrgKey, orgRef);
    notifyListeners();
    return true;
  }

  Future<void> reset() async {
    _resource = Resource(data: []);
    _currentOrg = null;
    notifyListeners();
    // Best-effort cleanup of stored selection to avoid using stale org on next login.
    await SecureStorageService.delete(Constants.currentOrgKey);
  }

  Future<Organization> uploadLogo(XFile logoFile) async {
    if (!isOrganizationSet) {
      throw StateError('Organization is not set');
    }

    _setResource(_resource.copyWith(isLoading: true, error: null));
    try {
      final updated = await OrganizationService.uploadLogoAndUpdate(current, logoFile);
      final updatedList = organizations
        .map((org) => org.id == updated.id ? updated : org)
        .toList(growable: false);
      _setResource(Resource(data: updatedList, isLoading: false));
      _currentOrg = updated.id;
      return updated;
    } catch (e) {
      _setResource(_resource.copyWith(isLoading: false, error: toException(e)));
      rethrow;
    }
  }

  Future<Organization> updateCurrent({
    String? name,
    String? description,
    String? timeZone,
    String? logoUrl,
  }) async {
    if (!isOrganizationSet) {
      throw StateError('Organization is not set');
    }

    _setResource(_resource.copyWith(isLoading: true, error: null));
    try {
      final updated = await OrganizationService.updateSettings(
        current,
        name: name,
        description: description,
        timeZone: timeZone,
        logoUrl: logoUrl,
      );
      final updatedList = organizations
        .map((org) => org.id == updated.id ? updated : org)
        .toList(growable: false);
      _setResource(Resource(data: updatedList, isLoading: false));
      _currentOrg = updated.id;
      return updated;
    } catch (e) {
      _setResource(_resource.copyWith(isLoading: false, error: toException(e)));
      rethrow;
    }
  }
}
