import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_data_connect/src/session/staff_session_store.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:firebase_auth/firebase_auth.dart';
import '../../domain/repositories/payments_repository.dart';

class PaymentsRepositoryImpl implements PaymentsRepository {
  final dc.ExampleConnector _dataConnect;
  final FirebaseAuth _auth = FirebaseAuth.instance;

  PaymentsRepositoryImpl() : _dataConnect = dc.ExampleConnector.instance;

  String? _cachedStaffId;

  Future<String> _getStaffId() async {
    // 1. Check Session Store
    final StaffSession? session = 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 user = _auth.currentUser;
    if (user == null) {
      throw Exception('User is not authenticated');
    }

    try {
      final response = await _dataConnect.getStaffByUserId(userId: user.uid).execute();
      if (response.data.staffs.isNotEmpty) {
        _cachedStaffId = response.data.staffs.first.id;
        return _cachedStaffId!;
      }
    } catch (e) {
      // Log or handle error
    }

    // 4. Fallback
    return user.uid;
  }

  /// Helper to convert Data Connect Timestamp to DateTime
  DateTime? _toDateTime(dynamic t) {
    if (t == null) return null;
    if (t is DateTime) return t;
    if (t is String) return DateTime.tryParse(t);

    try {
      if (t is Timestamp) {
        return t.toDateTime();
      }
    } catch (_) {}

    try {
      if (t.runtimeType.toString().contains('Timestamp')) {
        return (t as dynamic).toDate();
      }
    } catch (_) {}

    try {
      return DateTime.tryParse(t.toString());
    } catch (_) {}

    return null;
  }

  @override
  Future<PaymentSummary> getPaymentSummary() async {
    final String currentStaffId = await _getStaffId();

    // Fetch recent payments with a limit
    // Note: limit is chained on the query builder
    final QueryResult<dc.ListRecentPaymentsByStaffIdData, dc.ListRecentPaymentsByStaffIdVariables> result = 
      await _dataConnect.listRecentPaymentsByStaffId(
        staffId: currentStaffId,
    ).limit(100).execute();

    final List<dc.ListRecentPaymentsByStaffIdRecentPayments> payments = result.data.recentPayments;

    double weekly = 0;
    double monthly = 0;
    double pending = 0;
    double total = 0;

    final DateTime now = DateTime.now();
    final DateTime startOfWeek = now.subtract(const Duration(days: 7));
    final DateTime startOfMonth = DateTime(now.year, now.month, 1);

    for (final dc.ListRecentPaymentsByStaffIdRecentPayments p in payments) {
        final DateTime? date = _toDateTime(p.invoice.issueDate) ?? _toDateTime(p.createdAt);
        final double amount = p.invoice.amount;
        final String? status = p.status?.stringValue;

        if (status == 'PENDING') {
            pending += amount;
        } else if (status == 'PAID') {
            total += amount;
            if (date != null) {
                if (date.isAfter(startOfWeek)) weekly += amount;
                if (date.isAfter(startOfMonth)) monthly += amount;
            }
        }
    }

    return PaymentSummary(
      weeklyEarnings: weekly,
      monthlyEarnings: monthly,
      pendingEarnings: pending,
      totalEarnings: total,
    );
  }

  @override
  Future<List<StaffPayment>> getPaymentHistory(String period) async {
    final String currentStaffId = await _getStaffId();

    try {
      final QueryResult<dc.ListRecentPaymentsByStaffIdData, dc.ListRecentPaymentsByStaffIdVariables> response = 
          await _dataConnect
          .listRecentPaymentsByStaffId(staffId: currentStaffId)
          .execute();

      return response.data.recentPayments.map((dc.ListRecentPaymentsByStaffIdRecentPayments payment) {
        return StaffPayment(
          id: payment.id,
          staffId: payment.staffId,
          assignmentId: payment.applicationId,
          amount: payment.invoice.amount,
          status: PaymentAdapter.toPaymentStatus(payment.status?.stringValue ?? 'UNKNOWN'),
          paidAt: _toDateTime(payment.invoice.issueDate),
        );
      }).toList();
    } catch (e) {
      return <StaffPayment>[];
    }
  }
}

