import 'dart:async';

import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';

import '../../krow_data_connect.dart' as dc;
import '../mixins/data_error_handler.dart';

/// A centralized service for interacting with Firebase Data Connect.
///
/// This service provides common utilities and context management for all repositories.
class DataConnectService with DataErrorHandler {
  DataConnectService._();

  /// The singleton instance of the [DataConnectService].
  static final DataConnectService instance = DataConnectService._();

  /// The Data Connect connector used for data operations.
  final dc.ExampleConnector connector = dc.ExampleConnector.instance;

  /// The Firebase Auth instance.
  firebase_auth.FirebaseAuth get auth => _auth;
  final firebase_auth.FirebaseAuth _auth = firebase_auth.FirebaseAuth.instance;

  /// Cache for the current staff ID to avoid redundant lookups.
  String? _cachedStaffId;

  /// Cache for the current business ID to avoid redundant lookups.
  String? _cachedBusinessId;

  /// Gets the current staff ID from session store or persistent storage.
  Future<String> getStaffId() async {
    // 1. Check Session Store
    final dc.StaffSession? session = dc.StaffSessionStore.instance.session;
    if (session?.staff?.id != null) {
      return session!.staff!.id;
    }

    // 2. Check Cache
    if (_cachedStaffId != null) return _cachedStaffId!;

    // 3. Fetch from Data Connect using Firebase UID
    final firebase_auth.User? user = _auth.currentUser;
    if (user == null) {
      throw const NotAuthenticatedException(
        technicalMessage: 'User is not authenticated',
      );
    }

    try {
      final fdc.QueryResult<dc.GetStaffByUserIdData, dc.GetStaffByUserIdVariables>
          response = await executeProtected(
        () => connector.getStaffByUserId(userId: user.uid).execute(),
      );

      if (response.data.staffs.isNotEmpty) {
        _cachedStaffId = response.data.staffs.first.id;
        return _cachedStaffId!;
      }
    } catch (e) {
      throw Exception('Failed to fetch staff ID from Data Connect: $e');
    }

    // 4. Fallback (should ideally not happen if DB is seeded)
    return user.uid;
  }

  /// Gets the current business ID from session store or persistent storage.
  Future<String> getBusinessId() async {
    // 1. Check Session Store
    final dc.ClientSession? session = dc.ClientSessionStore.instance.session;
    if (session?.business?.id != null) {
      return session!.business!.id;
    }

    // 2. Check Cache
    if (_cachedBusinessId != null) return _cachedBusinessId!;

    // 3. Check Auth Status
    final firebase_auth.User? user = _auth.currentUser;
    if (user == null) {
      throw const NotAuthenticatedException(
        technicalMessage: 'User is not authenticated',
      );
    }

    // 4. Fallback (should ideally not happen if DB is seeded and session is initialized)
    // Ideally we'd have a getBusinessByUserId query here.
    return user.uid;
  }

  /// Converts a Data Connect timestamp/string/json to a [DateTime].
  DateTime? toDateTime(dynamic t) {
    if (t == null) return null;
    DateTime? dt;
    if (t is fdc.Timestamp) {
      dt = t.toDateTime();
    } else if (t is String) {
      dt = DateTime.tryParse(t);
    } else {
      try {
        dt = DateTime.tryParse(t.toJson() as String);
      } catch (_) {
        try {
          dt = DateTime.tryParse(t.toString());
        } catch (e) {
          dt = null;
        }
      }
    }

    if (dt != null) {
      return DateTimeUtils.toDeviceTime(dt);
    }
    return null;
  }

  /// Converts a [DateTime] to a Firebase Data Connect [Timestamp].
  fdc.Timestamp toTimestamp(DateTime dateTime) {
    final DateTime utc = dateTime.toUtc();
    final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
    final int nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
    return fdc.Timestamp(nanoseconds, seconds);
  }

  // --- 3. Unified Execution ---
  // Repositories call this to benefit from centralized error handling/logging
  Future<T> run<T>(
    Future<T> Function() action, {
    bool requiresAuthentication = true,
  }) {
    if (requiresAuthentication && auth.currentUser == null) {
      throw const NotAuthenticatedException(
        technicalMessage: 'User must be authenticated to perform this action',
      );
    }
    return executeProtected(action);
  }

  /// Clears the internal cache (e.g., on logout).
  void clearCache() {
    _cachedStaffId = null;
    _cachedBusinessId = null;
  }
}
