import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:async';

import 'package:tasq/models/profile.dart';
import 'package:tasq/models/task.dart';
import 'package:tasq/models/task_assignment.dart';
import 'package:tasq/providers/profile_provider.dart';
import 'package:tasq/providers/tasks_provider.dart';
import 'package:tasq/utils/snackbar.dart';
import 'package:tasq/utils/app_time.dart';
import 'package:tasq/widgets/task_assignment_section.dart';

void main() {
  setUp(() {
    AppTime.initialize();
  });

  testWidgets(
    'allows assigning IT staff to a task even when they are already on another active task',
    (tester) async {
      // prepare two it_staff profiles
      final profile1 = Profile(
        id: 'u1',
        fullName: 'User One',
        role: 'it_staff',
      );
      final profile2 = Profile(
        id: 'u2',
        fullName: 'User Two',
        role: 'it_staff',
      );

      // create two active tasks; staff u1 is already assigned to the first
      final now = DateTime.now();
      final task1 = Task.fromMap({
        'id': 't1',
        'status': 'queued',
        'title': 'First task',
        'description': '',
        'created_at': now.toIso8601String(),
        'priority': 1,
      });
      final task2 = Task.fromMap({
        'id': 't2',
        'status': 'queued',
        'title': 'Second task',
        'description': '',
        'created_at': now.toIso8601String(),
        'priority': 1,
      });

      final assignment1 = TaskAssignment(
        taskId: 't1',
        userId: 'u1',
        createdAt: now,
      );

      await tester.pumpWidget(
        ProviderScope(
          overrides: [
            profilesProvider.overrideWith(
              (ref) => Stream.value([profile1, profile2]),
            ),
            tasksProvider.overrideWith((ref) => Stream.value([task1, task2])),
            taskAssignmentsProvider.overrideWith(
              (ref) => Stream.value([assignment1]),
            ),
          ],
          child: MaterialApp(
            home: Scaffold(
              body: TaskAssignmentSection(taskId: 't2', canAssign: true),
            ),
          ),
        ),
      );
      // allow provider streams to deliver their first value
      await tester.pumpAndSettle();

      // open the assignment dialog
      await tester.tap(find.text('Assign'));
      await tester.pumpAndSettle();

      // both IT staff users should be listed even though u1 is already busy
      expect(find.byType(CheckboxListTile), findsNWidgets(2));
      expect(find.widgetWithText(CheckboxListTile, 'User One'), findsOneWidget);
      expect(find.widgetWithText(CheckboxListTile, 'User Two'), findsOneWidget);
    },
  );

  testWidgets('dialog shows message when there are no IT staff at all', (
    tester,
  ) async {
    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          profilesProvider.overrideWith(
            (ref) => Stream.value(const <Profile>[]),
          ),
          tasksProvider.overrideWith(
            (ref) => Stream.value(<Task>[
              Task.fromMap({
                'id': 't',
                'status': 'queued',
                'title': 't',
                'description': '',
                'created_at': DateTime.now().toIso8601String(),
                'priority': 1,
              }),
            ]),
          ),
          taskAssignmentsProvider.overrideWith(
            (ref) => Stream.value(const <TaskAssignment>[]),
          ),
        ],
        child: MaterialApp(
          home: Scaffold(
            body: TaskAssignmentSection(taskId: 't', canAssign: true),
          ),
        ),
      ),
    );
    await tester.pumpAndSettle();

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

    expect(find.text('No IT staff available.'), findsOneWidget);
  });

  testWidgets('save button shows spinner and snack on success', (tester) async {
    final profile = Profile(id: 'u1', fullName: 'User One', role: 'it_staff');
    final task = Task.fromMap({
      'id': 't',
      'status': 'queued',
      'title': 't',
      'description': '',
      'created_at': DateTime.now().toIso8601String(),
      'priority': 1,
    });
    final completer = Completer<void>();

    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          profilesProvider.overrideWith((ref) => Stream.value([profile])),
          tasksProvider.overrideWith((ref) => Stream.value([task])),
          taskAssignmentsProvider.overrideWith(
            (ref) => Stream.value(const <TaskAssignment>[]),
          ),
          taskAssignmentsControllerProvider.overrideWith(
            (ref) => _DelayedController(completer),
          ),
        ],
        child: MaterialApp(
          home: Scaffold(
            body: TaskAssignmentSection(taskId: 't', canAssign: true),
          ),
        ),
      ),
    );
    await tester.pumpAndSettle();

    // open dialog and select the only staff
    await tester.tap(find.text('Assign'));
    await tester.pumpAndSettle();
    await tester.tap(find.byType(CheckboxListTile));
    await tester.pump();

    // tap save, spinner should appear
    await tester.tap(find.text('Save'));
    await tester.pump();
    expect(find.byType(CircularProgressIndicator), findsOneWidget);

    // resolve future and let dialog close
    completer.complete();
    await tester.pumpAndSettle();

    // snackbar with success message should be visible
    expect(find.text('Assignment saved successfully'), findsOneWidget);
  });

  testWidgets('error snackbar shows when save fails', (tester) async {
    final profile = Profile(id: 'u1', fullName: 'User One', role: 'it_staff');
    final task = Task.fromMap({
      'id': 't',
      'status': 'queued',
      'title': 't',
      'description': '',
      'created_at': DateTime.now().toIso8601String(),
      'priority': 1,
    });

    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          profilesProvider.overrideWith((ref) => Stream.value([profile])),
          tasksProvider.overrideWith((ref) => Stream.value([task])),
          taskAssignmentsProvider.overrideWith(
            (ref) => Stream.value(const <TaskAssignment>[]),
          ),
          taskAssignmentsControllerProvider.overrideWith(
            (ref) => _FailingController(),
          ),
        ],
        child: MaterialApp(
          home: Scaffold(
            body: TaskAssignmentSection(taskId: 't', canAssign: true),
          ),
        ),
      ),
    );
    await tester.pumpAndSettle();

    await tester.tap(find.text('Assign'));
    await tester.pumpAndSettle();
    await tester.tap(find.byType(CheckboxListTile));
    await tester.pump();

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

    expect(find.text('Failed to save assignment'), findsOneWidget);
  });
}

// helper fakes used by the tests below
class _DelayedController implements TaskAssignmentsController {
  _DelayedController(this._completer);
  final Completer<void> _completer;

  @override
  Future<void> replaceAssignments({
    required String taskId,
    required String? ticketId,
    required List<String> newUserIds,
    required List<String> currentUserIds,
  }) {
    return _completer.future;
  }

  @override
  Future<void> removeAssignment({
    required String taskId,
    required String userId,
  }) {
    // not needed for these tests
    return Future.value();
  }
}

class _FailingController implements TaskAssignmentsController {
  @override
  Future<void> replaceAssignments({
    required String taskId,
    required String? ticketId,
    required List<String> newUserIds,
    required List<String> currentUserIds,
  }) async {
    throw Exception('server error');
  }

  @override
  Future<void> removeAssignment({
    required String taskId,
    required String userId,
  }) {
    return Future.value();
  }
}
