import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:kmobile/data/models/transaction.dart';
import 'package:kmobile/data/models/user.dart';
import 'package:kmobile/data/repositories/transaction_repository.dart';
import 'package:kmobile/features/accounts/screens/account_statement_screen.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:kmobile/l10n/app_localizations.dart';

// ─── Mock TransactionRepository ──────────────────────────────────────────────

class MockTransactionRepository implements TransactionRepository {
  List<Transaction> mockTransactions = [];
  bool shouldThrow = false;
  String? lastAccountNo;
  DateTime? lastFromDate;
  DateTime? lastToDate;
  int callCount = 0;

  @override
  Future<List<Transaction>> fetchTransactions(
    String accountNo, {
    DateTime? fromDate,
    DateTime? toDate,
  }) async {
    callCount++;
    lastAccountNo = accountNo;
    lastFromDate = fromDate;
    lastToDate = toDate;

    if (shouldThrow) {
      throw Exception('Network error');
    }
    return mockTransactions;
  }
}

// ─── Test Helpers ────────────────────────────────────────────────────────────

final getIt = GetIt.instance;

User _createTestUser({
  String accountNo = '1234567890',
  String name = 'Test User',
  String availableBalance = '50000',
  String branchId = 'BR001',
  String cifNumber = 'CIF123',
  String address = '123 Main Street',
}) {
  return User(
    accountNo: accountNo,
    accountType: 'SB',
    branchId: branchId,
    currency: 'INR',
    availableBalance: availableBalance,
    currentBalance: availableBalance,
    name: name,
    mobileNo: '9876543210',
    address: address,
    picode: '176215',
    cifNumber: cifNumber,
  );
}

Transaction _createTestTransaction({
  String id = '1',
  String name = 'UPI Payment',
  String date = '01-01-2026',
  String amount = '1000',
  String type = 'DR',
  String balance = '49000',
  String balanceType = 'CR',
}) {
  return Transaction(
    id: id,
    name: name,
    date: date,
    amount: amount,
    type: type,
    balance: balance,
    balanceType: balanceType,
  );
}

/// Wraps the widget with MaterialApp + localizations for testing
Widget _buildTestApp({
  required List<User> users,
  int selectedIndex = 0,
}) {
  return MaterialApp(
    localizationsDelegates: const [
      AppLocalizations.delegate,
      GlobalMaterialLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate,
      GlobalCupertinoLocalizations.delegate,
    ],
    supportedLocales: const [Locale('en')],
    locale: const Locale('en'),
    home: AccountStatementScreen(
      users: users,
      selectedIndex: selectedIndex,
    ),
  );
}

// ─── Tests ───────────────────────────────────────────────────────────────────

void main() {
  late MockTransactionRepository mockRepo;
  late User testUser;
  late User testUser2;

  setUp(() {
    // Reset GetIt before each test
    getIt.reset();

    mockRepo = MockTransactionRepository();
    getIt.registerSingleton<TransactionRepository>(mockRepo);

    testUser = _createTestUser();
    testUser2 = _createTestUser(
      accountNo: '9876543210',
      name: 'Second User',
      availableBalance: '75000',
    );
  });

  tearDown(() {
    getIt.reset();
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 1: Screen Rendering
  // ═══════════════════════════════════════════════════════════════════════════
  group('Screen Rendering', () {
    testWidgets('renders AppBar with "Account Statement" title', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.text('Account Statement'), findsOneWidget);
    });

    testWidgets('renders account number card with label', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.text('Account Number'), findsOneWidget);
    });

    testWidgets('renders available balance card', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      // The balance text shows ' ₹ 50000'
      expect(find.textContaining('50000'), findsOneWidget);
    });

    testWidgets('renders From Date and To Date filter boxes', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.text('From Date'), findsOneWidget);
      expect(find.text('To Date'), findsOneWidget);
    });

    testWidgets('renders Search button', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.text('Search'), findsOneWidget);
    });

    testWidgets('renders floating action button for PDF download',
        (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.byType(FloatingActionButton), findsOneWidget);
      expect(find.byIcon(Icons.download), findsOneWidget);
    });

    testWidgets('renders watermark logo with low opacity', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      // There should be an Opacity widget wrapping the logo
      final opacityFinder = find.byWidgetPredicate(
        (widget) => widget is Opacity && widget.opacity == 0.07,
      );
      expect(opacityFinder, findsOneWidget);
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 2: Account Dropdown
  // ═══════════════════════════════════════════════════════════════════════════
  group('Account Dropdown', () {
    testWidgets('displays selected user account number in dropdown',
        (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser, testUser2]));
      await tester.pumpAndSettle();

      expect(find.text('1234567890'), findsOneWidget);
    });

    testWidgets('dropdown shows all user accounts when tapped',
        (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser, testUser2]));
      await tester.pumpAndSettle();

      // Tap the dropdown
      await tester.tap(find.byType(DropdownButton<User>));
      await tester.pumpAndSettle();

      // Both accounts should be visible in the dropdown menu
      expect(find.text('1234567890'), findsWidgets);
      expect(find.text('9876543210'), findsWidgets);
    });

    testWidgets('selecting another account triggers transaction reload',
        (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser, testUser2]));
      await tester.pumpAndSettle();

      final initialCallCount = mockRepo.callCount;

      // Open dropdown and select second user
      await tester.tap(find.byType(DropdownButton<User>));
      await tester.pumpAndSettle();

      // Tap the second account (appears in the overlay)
      await tester.tap(find.text('9876543210').last);
      await tester.pumpAndSettle();

      // Verify repository was called again with the new account
      expect(mockRepo.callCount, greaterThan(initialCallCount));
      expect(mockRepo.lastAccountNo, '9876543210');
    });

    testWidgets('balance updates when user is switched', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser, testUser2]));
      await tester.pumpAndSettle();

      expect(find.textContaining('50000'), findsOneWidget);

      // Switch to second user
      await tester.tap(find.byType(DropdownButton<User>));
      await tester.pumpAndSettle();
      await tester.tap(find.text('9876543210').last);
      await tester.pumpAndSettle();

      // Balance should now show second user's balance
      expect(find.textContaining('75000'), findsOneWidget);
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 3: Loading State
  // ═══════════════════════════════════════════════════════════════════════════
  group('Loading State', () {
    testWidgets('shows shimmer loading effect initially', (tester) async {
      // Make the repo slow so loading state is visible
      mockRepo = MockTransactionRepository();
      getIt.allowReassignment = true;
      getIt.registerSingleton<TransactionRepository>(mockRepo);

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      // Don't call pumpAndSettle — we want to see the loading state
      await tester.pump();

      // Check for shimmer placeholders (CircleAvatar is used in loading tiles)
      expect(find.byType(CircleAvatar), findsWidgets);
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 4: Empty State
  // ═══════════════════════════════════════════════════════════════════════════
  group('Empty State', () {
    testWidgets('shows "No Transactions" when list is empty', (tester) async {
      mockRepo.mockTransactions = [];

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.text('No Transactions'), findsOneWidget);
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 5: Transaction List
  // ═══════════════════════════════════════════════════════════════════════════
  group('Transaction List', () {
    testWidgets('displays transaction list when data is loaded',
        (tester) async {
      mockRepo.mockTransactions = [
        _createTestTransaction(
          name: 'UPI Payment',
          date: '01-01-2026',
          amount: '1000',
          type: 'DR',
          balance: '49000',
        ),
        _createTestTransaction(
          id: '2',
          name: 'Salary Credit',
          date: '02-01-2026',
          amount: '50000',
          type: 'CR',
          balance: '99000',
        ),
      ];

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.text('01-01-2026'), findsOneWidget);
      expect(find.text('02-01-2026'), findsOneWidget);
      expect(find.text('UPI Payment'), findsOneWidget);
      expect(find.text('Salary Credit'), findsOneWidget);
    });

    testWidgets('displays amounts with rupee symbol', (tester) async {
      mockRepo.mockTransactions = [
        _createTestTransaction(amount: '1500', balance: '48500'),
      ];

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.text('₹1500'), findsOneWidget);
      expect(find.text('Bal: ₹48500'), findsOneWidget);
    });

    testWidgets('truncates long transaction names to 22 characters',
        (tester) async {
      mockRepo.mockTransactions = [
        _createTestTransaction(
          name: 'This is a very long transaction name exceeding limit',
        ),
      ];

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      // Only first 22 characters should be displayed
      expect(find.text('This is a very long tr'), findsOneWidget);
    });

    testWidgets('shows "Last 10 Transactions" label when no date filter set',
        (tester) async {
      mockRepo.mockTransactions = [
        _createTestTransaction(),
      ];

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.text('Last 10 Transactions'), findsOneWidget);
    });

    testWidgets('credit transactions show green icon', (tester) async {
      mockRepo.mockTransactions = [
        _createTestTransaction(type: 'CR'),
      ];

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      // Find the Icon for credit transactions (call_received)
      final iconFinder = find.byWidgetPredicate(
        (widget) =>
            widget is Icon && widget.color == const Color(0xFF10BB10),
      );
      expect(iconFinder, findsOneWidget);
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 6: Error State
  // ═══════════════════════════════════════════════════════════════════════════
  group('Error State', () {
    testWidgets('shows snackbar on transaction load failure', (tester) async {
      mockRepo.shouldThrow = true;

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      expect(find.byType(SnackBar), findsOneWidget);
      expect(find.textContaining('Failed to load transactions'), findsOneWidget);
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 7: Date Filters
  // ═══════════════════════════════════════════════════════════════════════════
  group('Date Filters', () {
    testWidgets('tapping "From Date" opens a date picker', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      await tester.tap(find.text('From Date'));
      await tester.pumpAndSettle();

      // The DatePicker dialog should be open
      expect(find.byType(DatePickerDialog), findsOneWidget);
    });

    testWidgets('tapping "To Date" without From Date shows error snackbar',
        (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      await tester.tap(find.text('To Date'));
      await tester.pumpAndSettle();

      // Should show snackbar asking to select From Date first
      expect(find.byType(SnackBar), findsOneWidget);
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 8: Search Button
  // ═══════════════════════════════════════════════════════════════════════════
  group('Search Button', () {
    testWidgets('pressing Search reloads transactions', (tester) async {
      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      final callCountBefore = mockRepo.callCount;

      await tester.tap(find.text('Search'));
      await tester.pumpAndSettle();

      expect(mockRepo.callCount, greaterThan(callCountBefore));
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 9: PDF Export
  // ═══════════════════════════════════════════════════════════════════════════
  group('PDF Export', () {
    testWidgets(
        'pressing download FAB with no transactions shows snackbar',
        (tester) async {
      mockRepo.mockTransactions = [];

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      await tester.tap(find.byType(FloatingActionButton));
      await tester.pumpAndSettle();

      expect(find.text('No transactions to export.'), findsOneWidget);
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 10: Selected Index
  // ═══════════════════════════════════════════════════════════════════════════
  group('Selected Index', () {
    testWidgets('uses selectedIndex to pick initial user', (tester) async {
      await tester.pumpWidget(
        _buildTestApp(users: [testUser, testUser2], selectedIndex: 1),
      );
      await tester.pumpAndSettle();

      // Second user's balance should be shown
      expect(find.textContaining('75000'), findsOneWidget);

      // Repository should have been called with second user's account
      expect(mockRepo.lastAccountNo, '9876543210');
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // GROUP 11: Navigation
  // ═══════════════════════════════════════════════════════════════════════════
  group('Navigation', () {
    testWidgets('tapping a transaction navigates to details screen',
        (tester) async {
      mockRepo.mockTransactions = [
        _createTestTransaction(name: 'Test Payment'),
      ];

      await tester.pumpWidget(_buildTestApp(users: [testUser]));
      await tester.pumpAndSettle();

      // Tap on the transaction ListTile
      await tester.tap(find.text('Test Payment'));
      await tester.pumpAndSettle();

      // We should have navigated away from the statement screen
      // The TransactionDetailsScreen should now be visible
      expect(find.byType(AccountStatementScreen), findsNothing);
    });
  });
}
