import 'package:flutter_test/flutter_test.dart';

import 'package:feature_wordpress/feature_wordpress.dart';

void main() {
  late FakeProductPublishingRepository repository;

  setUp(() {
    repository = FakeProductPublishingRepository();
  });

  group('FakeProductPublishingRepository', () {
    test('getProductDrafts returns six sample products', () async {
      final drafts = await repository.getProductDrafts();
      expect(drafts.length, 6);
    });

    test('getProductDrafts returns products with expected names', () async {
      final drafts = await repository.getProductDrafts();
      final names = drafts.map((d) => d.name).toList();
      expect(names, contains('Floral Bowl Cozy'));
      expect(names, contains('Citrus Coaster Set'));
      expect(names, contains('Ocean Nightlight'));
      expect(names, contains('Fabric Jar Gripper'));
      expect(names, contains('Skillet Handle Sleeve'));
      expect(names, contains('Sublimated Slate Coaster'));
    });

    test('getProductDrafts returns products with various statuses', () async {
      final drafts = await repository.getProductDrafts();
      final statuses = drafts.map((d) => d.status).toSet();
      expect(statuses, contains(PublishStatus.published));
      expect(statuses, contains(PublishStatus.pendingReview));
      expect(statuses, contains(PublishStatus.draft));
      expect(statuses, contains(PublishStatus.unpublished));
    });

    test('publishDraft changes status to published', () async {
      // Product 4 starts as draft.
      final updated = await repository.publishDraft('4');
      expect(updated.status, PublishStatus.published);
      expect(updated.name, 'Fabric Jar Gripper');
    });

    test('publishDraft throws for unknown id', () async {
      expect(() => repository.publishDraft('unknown'), throwsA(isA<StateError>()));
    });

    group('updateProductStatus', () {
      test('draft to published updates only the target product', () async {
        // Product 4 starts as draft.
        final updated = await repository.updateProductStatus('4', PublishStatus.published);

        expect(updated.id, '4');
        expect(updated.status, PublishStatus.published);
        expect(updated.name, 'Fabric Jar Gripper');

        // Verify the change is persisted in the list.
        final drafts = await repository.getProductDrafts();
        final product4 = drafts.firstWhere((d) => d.id == '4');
        expect(product4.status, PublishStatus.published);
      });

      test('published to draft updates only the target product', () async {
        // Product 1 starts as published.
        final updated = await repository.updateProductStatus('1', PublishStatus.draft);

        expect(updated.id, '1');
        expect(updated.status, PublishStatus.draft);
        expect(updated.name, 'Floral Bowl Cozy');

        // Verify the change is persisted in the list.
        final drafts = await repository.getProductDrafts();
        final product1 = drafts.firstWhere((d) => d.id == '1');
        expect(product1.status, PublishStatus.draft);
      });

      test('unpublished to draft updates only the target product', () async {
        // Product 6 starts as unpublished.
        final updated = await repository.updateProductStatus('6', PublishStatus.draft);

        expect(updated.id, '6');
        expect(updated.status, PublishStatus.draft);
        expect(updated.name, 'Sublimated Slate Coaster');

        // Verify the change is persisted in the list.
        final drafts = await repository.getProductDrafts();
        final product6 = drafts.firstWhere((d) => d.id == '6');
        expect(product6.status, PublishStatus.draft);
      });

      test('preserves other products unchanged', () async {
        final draftsBefore = await repository.getProductDrafts();

        // Update product 4 only.
        await repository.updateProductStatus('4', PublishStatus.published);

        final draftsAfter = await repository.getProductDrafts();

        // All other products should be identical.
        for (final before in draftsBefore) {
          if (before.id == '4') continue;
          final after = draftsAfter.firstWhere((d) => d.id == before.id);
          expect(after.name, before.name);
          expect(after.status, before.status);
          expect(after.price, before.price);
          expect(after.sku, before.sku);
          expect(after.category, before.category);
          expect(after.description, before.description);
          expect(after.imageUrl, before.imageUrl);
        }
      });

      test('preserves all fields of the updated product except status and lastModified', () async {
        final draftsBefore = await repository.getProductDrafts();
        final before = draftsBefore.firstWhere((d) => d.id == '4');

        final updated = await repository.updateProductStatus('4', PublishStatus.published);

        expect(updated.name, before.name);
        expect(updated.price, before.price);
        expect(updated.sku, before.sku);
        expect(updated.category, before.category);
        expect(updated.description, before.description);
        expect(updated.imageUrl, before.imageUrl);
        // Status changed, lastModified updated.
        expect(updated.status, PublishStatus.published);
        expect(updated.lastModified.isAfter(before.lastModified), isTrue);
      });

      test('throws StateError for unknown id', () async {
        expect(
          () => repository.updateProductStatus('unknown', PublishStatus.draft),
          throwsA(isA<StateError>()),
        );
      });
    });

    group('updateProductPrice', () {
      test('updates price of the target product', () async {
        final updated = await repository.updateProductPrice('4', 11.99);

        expect(updated.id, '4');
        expect(updated.price, 11.99);
        expect(updated.name, 'Fabric Jar Gripper');
      });

      test('persists the price change in the list', () async {
        await repository.updateProductPrice('4', 11.99);

        final drafts = await repository.getProductDrafts();
        final product4 = drafts.firstWhere((d) => d.id == '4');
        expect(product4.price, 11.99);
      });

      test('preserves all fields except price and lastModified', () async {
        final draftsBefore = await repository.getProductDrafts();
        final before = draftsBefore.firstWhere((d) => d.id == '4');

        final updated = await repository.updateProductPrice('4', 11.99);

        expect(updated.name, before.name);
        expect(updated.sku, before.sku);
        expect(updated.category, before.category);
        expect(updated.description, before.description);
        expect(updated.imageUrl, before.imageUrl);
        expect(updated.status, before.status);
        expect(updated.price, 11.99);
        expect(updated.lastModified.isAfter(before.lastModified), isTrue);
      });

      test('preserves other products unchanged', () async {
        final draftsBefore = await repository.getProductDrafts();

        await repository.updateProductPrice('4', 11.99);

        final draftsAfter = await repository.getProductDrafts();
        for (final before in draftsBefore) {
          if (before.id == '4') continue;
          final after = draftsAfter.firstWhere((d) => d.id == before.id);
          expect(after.price, before.price);
          expect(after.name, before.name);
          expect(after.status, before.status);
        }
      });

      test('throws StateError for unknown id', () async {
        expect(() => repository.updateProductPrice('unknown', 9.99), throwsA(isA<StateError>()));
      });
    });

    group('updateProductName', () {
      test('updates name of the target product', () async {
        final updated = await repository.updateProductName('4', 'Deluxe Jar Gripper');

        expect(updated.id, '4');
        expect(updated.name, 'Deluxe Jar Gripper');
      });

      test('persists the name change in the list', () async {
        await repository.updateProductName('4', 'Deluxe Jar Gripper');

        final drafts = await repository.getProductDrafts();
        final product4 = drafts.firstWhere((d) => d.id == '4');
        expect(product4.name, 'Deluxe Jar Gripper');
      });

      test('preserves all fields except name and lastModified', () async {
        final draftsBefore = await repository.getProductDrafts();
        final before = draftsBefore.firstWhere((d) => d.id == '4');

        final updated = await repository.updateProductName('4', 'Deluxe Jar Gripper');

        expect(updated.price, before.price);
        expect(updated.sku, before.sku);
        expect(updated.category, before.category);
        expect(updated.description, before.description);
        expect(updated.imageUrl, before.imageUrl);
        expect(updated.status, before.status);
        expect(updated.name, 'Deluxe Jar Gripper');
        expect(updated.lastModified.isAfter(before.lastModified), isTrue);
      });

      test('preserves other products unchanged', () async {
        final draftsBefore = await repository.getProductDrafts();

        await repository.updateProductName('4', 'Deluxe Jar Gripper');

        final draftsAfter = await repository.getProductDrafts();
        for (final before in draftsBefore) {
          if (before.id == '4') continue;
          final after = draftsAfter.firstWhere((d) => d.id == before.id);
          expect(after.name, before.name);
          expect(after.price, before.price);
          expect(after.status, before.status);
        }
      });

      test('throws StateError for unknown id', () async {
        expect(
          () => repository.updateProductName('unknown', 'New Name'),
          throwsA(isA<StateError>()),
        );
      });
    });

    group('updateProductDescription', () {
      test('updates description of the target product', () async {
        final updated = await repository.updateProductDescription('4', 'A brand new description.');

        expect(updated.id, '4');
        expect(updated.description, 'A brand new description.');
      });

      test('persists the description change in the list', () async {
        await repository.updateProductDescription('4', 'A brand new description.');

        final drafts = await repository.getProductDrafts();
        final product4 = drafts.firstWhere((d) => d.id == '4');
        expect(product4.description, 'A brand new description.');
      });

      test('preserves all fields except description and lastModified', () async {
        final draftsBefore = await repository.getProductDrafts();
        final before = draftsBefore.firstWhere((d) => d.id == '4');

        final updated = await repository.updateProductDescription('4', 'A brand new description.');

        expect(updated.name, before.name);
        expect(updated.price, before.price);
        expect(updated.sku, before.sku);
        expect(updated.category, before.category);
        expect(updated.imageUrl, before.imageUrl);
        expect(updated.status, before.status);
        expect(updated.description, 'A brand new description.');
        expect(updated.lastModified.isAfter(before.lastModified), isTrue);
      });

      test('preserves other products unchanged', () async {
        final draftsBefore = await repository.getProductDrafts();

        await repository.updateProductDescription('4', 'A brand new description.');

        final draftsAfter = await repository.getProductDrafts();
        for (final before in draftsBefore) {
          if (before.id == '4') continue;
          final after = draftsAfter.firstWhere((d) => d.id == before.id);
          expect(after.description, before.description);
          expect(after.name, before.name);
          expect(after.price, before.price);
          expect(after.status, before.status);
        }
      });

      test('throws StateError for unknown id', () async {
        expect(
          () => repository.updateProductDescription('unknown', 'New desc'),
          throwsA(isA<StateError>()),
        );
      });
    });

    group('updateProductCategory', () {
      test('updates category of existing product', () async {
        final before = (await repository.getProductDrafts()).firstWhere((d) => d.id == '4');
        expect(before.category, 'Kitchen Accessories');

        final updated = await repository.updateProductCategory('4', 'Grippers');
        expect(updated.category, 'Grippers');
        expect(updated.id, '4');
      });

      test('persists category change across subsequent reads', () async {
        await repository.updateProductCategory('4', 'Grippers');

        final drafts = await repository.getProductDrafts();
        final product = drafts.firstWhere((d) => d.id == '4');
        expect(product.category, 'Grippers');
      });

      test('updates lastModified timestamp', () async {
        final before = (await repository.getProductDrafts()).firstWhere((d) => d.id == '4');

        final updated = await repository.updateProductCategory('4', 'Grippers');
        expect(updated.lastModified.isAfter(before.lastModified), isTrue);
      });

      test('preserves other fields unchanged', () async {
        final before = (await repository.getProductDrafts()).firstWhere((d) => d.id == '4');

        final updated = await repository.updateProductCategory('4', 'Grippers');
        expect(updated.name, before.name);
        expect(updated.description, before.description);
        expect(updated.price, before.price);
        expect(updated.sku, before.sku);
        expect(updated.imageUrl, before.imageUrl);
        expect(updated.status, before.status);
        expect(updated.category, 'Grippers');
        expect(updated.lastModified.isAfter(before.lastModified), isTrue);
      });

      test('preserves other products unchanged', () async {
        final draftsBefore = await repository.getProductDrafts();

        await repository.updateProductCategory('4', 'Grippers');

        final draftsAfter = await repository.getProductDrafts();
        for (final before in draftsBefore) {
          if (before.id == '4') continue;
          final after = draftsAfter.firstWhere((d) => d.id == before.id);
          expect(after.category, before.category);
          expect(after.name, before.name);
          expect(after.price, before.price);
          expect(after.status, before.status);
        }
      });

      test('throws StateError for unknown id', () async {
        expect(
          () => repository.updateProductCategory('unknown', 'New Cat'),
          throwsA(isA<StateError>()),
        );
      });
    });
  });
}
