import 'package:firebase_auth/firebase_auth.dart' as firebase;
import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart' as domain;

import '../../domain/repositories/hub_repository_interface.dart';

/// Implementation of [HubRepositoryInterface] backed by Data Connect.
class HubRepositoryImpl implements HubRepositoryInterface {
  HubRepositoryImpl({
    required firebase.FirebaseAuth firebaseAuth,
    required dc.ExampleConnector dataConnect,
  }) : _firebaseAuth = firebaseAuth,
       _dataConnect = dataConnect;

  final firebase.FirebaseAuth _firebaseAuth;
  final dc.ExampleConnector _dataConnect;

  @override
  Future<List<domain.Hub>> getHubs() async {
    final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser();
    final String teamId = await _getOrCreateTeamId(business);
    return _fetchHubsForTeam(teamId: teamId, businessId: business.id);
  }

  @override
  Future<domain.Hub> createHub({
    required String name,
    required String address,
  }) async {
    final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser();
    final String teamId = await _getOrCreateTeamId(business);
    final String? city = business.city;

    final OperationResult<dc.CreateTeamHubData, dc.CreateTeamHubVariables> result = await _dataConnect
        .createTeamHub(
          teamId: teamId,
          hubName: name,
          address: address,
        )
        .city(city?.isNotEmpty == true ? city : '')
        .execute();
    final String? createdId = result.data?.teamHub_insert.id;
    if (createdId == null) {
      throw Exception('Hub creation failed.');
    }

    final List<domain.Hub> hubs = await _fetchHubsForTeam(
      teamId: teamId,
      businessId: business.id,
    );
    domain.Hub? createdHub;
    for (final domain.Hub hub in hubs) {
      if (hub.id == createdId) {
        createdHub = hub;
        break;
      }
    }
    return createdHub ??
        domain.Hub(
          id: createdId,
          businessId: business.id,
          name: name,
          address: address,
          nfcTagId: null,
          status: domain.HubStatus.active,
        );
  }

  @override
  Future<void> deleteHub(String id) async {
    await _dataConnect.deleteTeamHub(id: id).execute();
  }

  @override
  Future<void> assignNfcTag({
    required String hubId,
    required String nfcTagId,
  }) {
    throw UnimplementedError('NFC tag assignment is not supported for team hubs.');
  }

  Future<dc.GetBusinessesByUserIdBusinesses> _getBusinessForCurrentUser() async {
    final dc.ClientSession? session = dc.ClientSessionStore.instance.session;
    final dc.ClientBusinessSession? cachedBusiness = session?.business;
    if (cachedBusiness != null) {
      return dc.GetBusinessesByUserIdBusinesses(
        id: cachedBusiness.id,
        businessName: cachedBusiness.businessName,
        userId: _firebaseAuth.currentUser?.uid ?? '',
        rateGroup: const dc.Known<dc.BusinessRateGroup>(dc.BusinessRateGroup.STANDARD),
        status: const dc.Known<dc.BusinessStatus>(dc.BusinessStatus.ACTIVE),
        contactName: cachedBusiness.contactName,
        companyLogoUrl: cachedBusiness.companyLogoUrl,
        phone: null,
        email: cachedBusiness.email,
        hubBuilding: null,
        address: null,
        city: cachedBusiness.city,
        area: null,
        sector: null,
        notes: null,
        createdAt: null,
        updatedAt: null,
      );
    }

    final firebase.User? user = _firebaseAuth.currentUser;
    if (user == null) {
      throw Exception('User is not authenticated.');
    }

    final QueryResult<dc.GetBusinessesByUserIdData, dc.GetBusinessesByUserIdVariables> result = await _dataConnect.getBusinessesByUserId(
      userId: user.uid,
    ).execute();
    if (result.data.businesses.isEmpty) {
      await _firebaseAuth.signOut();
      throw Exception('No business found for this user. Please sign in again.');
    }

    final dc.GetBusinessesByUserIdBusinesses business = result.data.businesses.first;
    if (session != null) {
      dc.ClientSessionStore.instance.setSession(
        dc.ClientSession(
          user: session.user,
          userPhotoUrl: session.userPhotoUrl,
          business: dc.ClientBusinessSession(
            id: business.id,
            businessName: business.businessName,
            email: business.email,
            city: business.city,
            contactName: business.contactName,
            companyLogoUrl: business.companyLogoUrl,
          ),
        ),
      );
    }

    return business;
  }

  Future<String> _getOrCreateTeamId(
    dc.GetBusinessesByUserIdBusinesses business,
  ) async {
    final QueryResult<dc.GetTeamsByOwnerIdData, dc.GetTeamsByOwnerIdVariables> teamsResult = await _dataConnect.getTeamsByOwnerId(
      ownerId: business.id,
    ).execute();
    if (teamsResult.data.teams.isNotEmpty) {
      return teamsResult.data.teams.first.id;
    }

    final dc.CreateTeamVariablesBuilder createTeamBuilder = _dataConnect.createTeam(
      teamName: '${business.businessName} Team',
      ownerId: business.id,
      ownerName: business.contactName ?? '',
      ownerRole: 'OWNER',
    );
    if (business.email != null) {
      createTeamBuilder.email(business.email);
    }

    final OperationResult<dc.CreateTeamData, dc.CreateTeamVariables> createTeamResult = await createTeamBuilder.execute();
    final String? teamId = createTeamResult.data?.team_insert.id;
    if (teamId == null) {
      throw Exception('Team creation failed.');
    }

    return teamId;
  }

  Future<List<domain.Hub>> _fetchHubsForTeam({
    required String teamId,
    required String businessId,
  }) async {
    final QueryResult<dc.GetTeamHubsByTeamIdData, dc.GetTeamHubsByTeamIdVariables> hubsResult = await _dataConnect.getTeamHubsByTeamId(
      teamId: teamId,
    ).execute();

    return hubsResult.data.teamHubs
        .map(
          (dc.GetTeamHubsByTeamIdTeamHubs hub) => domain.Hub(
            id: hub.id,
            businessId: businessId,
            name: hub.hubName,
            address: hub.address,
            nfcTagId: null,
            status:
                hub.isActive
                    ? domain.HubStatus.active
                    : domain.HubStatus.inactive,
          ),
        )
        .toList();
  }
}
