import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fotodocumentation/controller/login_controller.dart';
import 'package:fotodocumentation/dto/jwt_token_pair_dto.dart';
import 'package:fotodocumentation/pages/login/login_widget.dart';
import 'package:fotodocumentation/utils/di_container.dart';
import 'package:fotodocumentation/utils/global_router.dart';
import 'package:fotodocumentation/utils/login_credentials.dart';
import 'package:mockito/mockito.dart';

import '../testing/test_utils.dart';
import '../testing/test_utils.mocks.dart';

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();
  DiContainer.instance.initState();

  late MockLoginController mockLoginController;
  late MockLoginCredentials mockLoginCredentials;

  setUp(() {
    mockLoginController = MockLoginController();
    mockLoginCredentials = MockLoginCredentials();

    when(mockLoginCredentials.isLoggedIn).thenReturn(false);

    DiContainer.instance.put(LoginController, mockLoginController);
    DiContainer.instance.put(LoginCredentials, mockLoginCredentials);
  });

  group('LoginWidget', () {
    testWidgets('displays login title', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      await pumpApp(tester, const LoginWidget());
      await tester.pumpAndSettle();

      // Verify the login title is displayed (German localization)
      expect(find.text('BILDERUPLOAD'), findsOneWidget);
    });

    testWidgets('displays username and password fields', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      await pumpApp(tester, const LoginWidget());
      await tester.pumpAndSettle();

      // Verify username field exists
      expect(find.byKey(const Key("username")), findsOneWidget);

      // Verify password field exists
      expect(find.byKey(const Key("password")), findsOneWidget);
    });

    testWidgets('displays login button', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      await pumpApp(tester, const LoginWidget());
      await tester.pumpAndSettle();

      // Verify login button exists
      expect(find.byKey(const Key("SubmitWidgetButton")), findsOneWidget);
    });

    testWidgets('can enter username and password', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      await pumpApp(tester, const LoginWidget());
      await tester.pumpAndSettle();

      // Enter username
      await tester.enterText(find.byKey(const Key("username")), 'testuser');
      await tester.pumpAndSettle();

      // Enter password
      await tester.enterText(find.byKey(const Key("password")), 'testpassword');
      await tester.pumpAndSettle();

      // Verify text was entered
      expect(find.text('testuser'), findsOneWidget);
    });

    testWidgets('calls authenticate on login button tap', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      final jwtTokenPairDto = JwtTokenPairDto(
        accessToken: 'test_access_token',
        refreshToken: 'test_refresh_token',
      );

      when(mockLoginController.authenticate('testuser', 'testpassword'))
          .thenAnswer((_) async => (jwtTokenPairDto: jwtTokenPairDto));

      await pumpAppConfig(tester, GlobalRouter.pathLogin);
      await tester.pumpAndSettle();

      // Enter credentials
      await tester.enterText(find.byKey(const Key("username")), 'testuser');
      await tester.enterText(find.byKey(const Key("password")), 'testpassword');
      await tester.pumpAndSettle();

      // Tap login button
      await tester.tap(find.byKey(const Key("SubmitWidgetButton")));
      await tester.pumpAndSettle();

      // Verify authenticate was called with correct credentials
      verify(mockLoginController.authenticate('testuser', 'testpassword')).called(1);
    });

    testWidgets('sets logged in on successful authentication', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      final jwtTokenPairDto = JwtTokenPairDto(
        accessToken: 'test_access_token',
        refreshToken: 'test_refresh_token',
      );

      when(mockLoginController.authenticate('testuser', 'testpassword'))
          .thenAnswer((_) async => (jwtTokenPairDto: jwtTokenPairDto));

      await pumpAppConfig(tester, GlobalRouter.pathLogin);
      await tester.pumpAndSettle();

      // Enter credentials
      await tester.enterText(find.byKey(const Key("username")), 'testuser');
      await tester.enterText(find.byKey(const Key("password")), 'testpassword');
      await tester.pumpAndSettle();

      // Tap login button
      await tester.tap(find.byKey(const Key("SubmitWidgetButton")));
      await tester.pumpAndSettle();

      // Verify setLoggedIn was called
      verify(mockLoginCredentials.setLoggedIn(true)).called(1);
    });

    testWidgets('displays error message on failed authentication', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      when(mockLoginController.authenticate('testuser', 'wrongpassword'))
          .thenAnswer((_) async => (jwtTokenPairDto: null));

      await pumpAppConfig(tester, GlobalRouter.pathLogin);
      await tester.pumpAndSettle();

      // Enter credentials
      await tester.enterText(find.byKey(const Key("username")), 'testuser');
      await tester.enterText(find.byKey(const Key("password")), 'wrongpassword');
      await tester.pumpAndSettle();

      // Tap login button
      await tester.tap(find.byKey(const Key("SubmitWidgetButton")));
      await tester.pumpAndSettle();

      // Verify error message is displayed (German localization)
      expect(find.text('Falscher Benutzername oder Passwort'), findsOneWidget);
    });

    testWidgets('does not call setLoggedIn on failed authentication', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      when(mockLoginController.authenticate('testuser', 'wrongpassword'))
          .thenAnswer((_) async => (jwtTokenPairDto: null));

      await pumpAppConfig(tester, GlobalRouter.pathLogin);
      await tester.pumpAndSettle();

      // Enter credentials
      await tester.enterText(find.byKey(const Key("username")), 'testuser');
      await tester.enterText(find.byKey(const Key("password")), 'wrongpassword');
      await tester.pumpAndSettle();

      // Tap login button
      await tester.tap(find.byKey(const Key("SubmitWidgetButton")));
      await tester.pumpAndSettle();

      // Verify setLoggedIn was NOT called
      verifyNever(mockLoginCredentials.setLoggedIn(any));
    });

    testWidgets('password field obscures text', (WidgetTester tester) async {
      setScreenSize(tester, 1024, 1024);

      await pumpApp(tester, const LoginWidget());
      await tester.pumpAndSettle();

      // Find the EditableText descendant of the password field
      // TextFormField wraps TextField which contains EditableText
      final passwordFieldFinder = find.byKey(const Key("password"));
      final editableTextFinder = find.descendant(
        of: passwordFieldFinder,
        matching: find.byType(EditableText),
      );
      final editableText = tester.widget<EditableText>(editableTextFinder);

      // Verify obscureText is true
      expect(editableText.obscureText, isTrue);
    });
  });
}
