import 'dart:convert';

import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';

import 'package:feature_wordpress/feature_wordpress.dart';

void main() {
  /// Builds a minimal WooCommerce product JSON list response.
  List<Map<String, dynamic>> buildProductsJson(int count) {
    return List.generate(count, (i) {
      return {
        'id': i + 1,
        'name': 'Product ${i + 1}',
        'description': '<p>Description ${i + 1}</p>',
        'price': '${(i + 1) * 10}.99',
        'sku': 'SKU-${i + 1}',
        'status': 'publish',
        'date_modified': '2026-04-01T10:00:00',
        'date_created': '2026-03-01T08:00:00',
        'categories': [
          {'id': 1, 'name': 'Test Category', 'slug': 'test-category'},
        ],
        'images': [
          {'id': 1, 'src': 'https://example.com/img${i + 1}.jpg'},
        ],
      };
    });
  }

  group('WooCommerceApiClient', () {
    test('getProducts returns parsed product list on 200', () async {
      final mockClient = MockClient((request) async {
        expect(request.url.path, '/wp-json/wc/v3/products');
        expect(request.url.queryParameters['page'], '1');
        expect(request.url.queryParameters['per_page'], '100');
        expect(request.headers['Authorization'], startsWith('Basic '));

        return http.Response(jsonEncode(buildProductsJson(3)), 200);
      });

      final apiClient = WooCommerceApiClient(
        siteUrl: 'https://store.example.com',
        consumerKey: 'ck_test',
        consumerSecret: 'cs_test',
        httpClient: mockClient,
      );

      final products = await apiClient.getProducts();
      expect(products.length, 3);
      expect(products[0]['name'], 'Product 1');
      expect(products[2]['name'], 'Product 3');
    });

    test('getProducts throws WooCommerceApiException on non-200', () async {
      final mockClient = MockClient((request) async {
        return http.Response('{"code":"unauthorized"}', 401);
      });

      final apiClient = WooCommerceApiClient(
        siteUrl: 'https://store.example.com',
        consumerKey: 'ck_bad',
        consumerSecret: 'cs_bad',
        httpClient: mockClient,
      );

      expect(() => apiClient.getProducts(), throwsA(isA<WooCommerceApiException>()));
    });

    test('getAllProducts paginates until a short page', () async {
      var requestCount = 0;
      final mockClient = MockClient((request) async {
        requestCount++;
        final page = int.parse(request.url.queryParameters['page']!);
        // Page 1 returns 2 items (perPage=2), page 2 returns 1 item (short).
        final count = page == 1 ? 2 : 1;
        return http.Response(jsonEncode(buildProductsJson(count)), 200);
      });

      final apiClient = WooCommerceApiClient(
        siteUrl: 'https://store.example.com',
        consumerKey: 'ck_test',
        consumerSecret: 'cs_test',
        httpClient: mockClient,
      );

      final products = await apiClient.getAllProducts(perPage: 2);
      expect(products.length, 3);
      expect(requestCount, 2);
    });
  });

  group('WordPressProductPublishingRepository', () {
    test('getProductDrafts returns mapped ProductDraft list', () async {
      final mockClient = MockClient((request) async {
        return http.Response(jsonEncode(buildProductsJson(2)), 200);
      });

      final apiClient = WooCommerceApiClient(
        siteUrl: 'https://store.example.com',
        consumerKey: 'ck_test',
        consumerSecret: 'cs_test',
        httpClient: mockClient,
      );

      final repository = WordPressProductPublishingRepository(apiClient: apiClient);

      final drafts = await repository.getProductDrafts();
      expect(drafts.length, 2);
      expect(drafts[0].id, '1');
      expect(drafts[0].name, 'Product 1');
      expect(drafts[0].description, 'Description 1');
      expect(drafts[0].price, 10.99);
      expect(drafts[0].sku, 'SKU-1');
      expect(drafts[0].category, 'Test Category');
      expect(drafts[0].imageUrl, 'https://example.com/img1.jpg');
      expect(drafts[0].status, PublishStatus.published);
      expect(drafts[1].id, '2');
      expect(drafts[1].name, 'Product 2');
    });

    group('publishDraft', () {
      /// Builds a single WooCommerce product JSON response with the given
      /// [id] and status set to `'publish'`.
      Map<String, dynamic> buildPublishedProductJson(int id) {
        return {
          'id': id,
          'name': 'Product $id',
          'description': '<p>Description</p>',
          'price': '10.99',
          'sku': 'SKU-$id',
          'status': 'publish',
          'date_modified': '2026-04-01T10:00:00',
          'date_created': '2026-03-01T08:00:00',
          'categories': [
            {'id': 1, 'name': 'Test Category', 'slug': 'test-category'},
          ],
          'images': [
            {'id': 1, 'src': 'https://example.com/img$id.jpg'},
          ],
        };
      }

      test('sends PUT with status publish and returns mapped ProductDraft', () async {
        Map<String, dynamic>? capturedBody;
        Uri? capturedUri;
        String? capturedMethod;

        final mockClient = MockClient((request) async {
          capturedMethod = request.method;
          capturedUri = request.url;
          capturedBody = jsonDecode(request.body) as Map<String, dynamic>;

          return http.Response(jsonEncode(buildPublishedProductJson(42)), 200);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);
        final result = await repository.publishDraft('42');

        // Verify the HTTP request shape.
        expect(capturedMethod, 'PUT');
        expect(capturedUri!.path, '/wp-json/wc/v3/products/42');
        expect(capturedBody, {'status': 'publish'});

        // Verify the returned domain object is cleanly mapped.
        expect(result, isA<ProductDraft>());
        expect(result.id, '42');
        expect(result.name, 'Product 42');
        expect(result.status, PublishStatus.published);
      });

      test('propagates WooCommerceApiException on non-200 response', () async {
        final mockClient = MockClient((request) async {
          return http.Response('{"code":"rest_cannot_edit"}', 403);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);

        expect(() => repository.publishDraft('42'), throwsA(isA<WooCommerceApiException>()));
      });

      test('returns ProductDraft, not raw WooCommerce JSON (package boundary)', () async {
        final mockClient = MockClient((request) async {
          final json = buildPublishedProductJson(9);
          json['permalink'] = 'https://store.example.com/product/9';
          json['meta_data'] = [
            {'id': 1, 'key': '_internal', 'value': 'secret'},
          ];
          return http.Response(jsonEncode(json), 200);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);
        final result = await repository.publishDraft('9');

        // The result is a domain ProductDraft — no WooCommerce-specific
        // fields like permalink or meta_data are exposed.
        expect(result, isA<ProductDraft>());
        expect(result.id, '9');
        expect(result.status, PublishStatus.published);
      });
    });

    test('getProductDrafts propagates API errors', () async {
      final mockClient = MockClient((request) async {
        return http.Response('Server Error', 500);
      });

      final apiClient = WooCommerceApiClient(
        siteUrl: 'https://store.example.com',
        consumerKey: 'ck_test',
        consumerSecret: 'cs_test',
        httpClient: mockClient,
      );

      final repository = WordPressProductPublishingRepository(apiClient: apiClient);

      expect(() => repository.getProductDrafts(), throwsA(isA<WooCommerceApiException>()));
    });

    group('updateProductStatus', () {
      /// Builds a single WooCommerce product JSON response with the given
      /// [id] and [status].
      Map<String, dynamic> buildSingleProductJson({required int id, required String status}) {
        return {
          'id': id,
          'name': 'Product $id',
          'description': '<p>Description</p>',
          'price': '10.99',
          'sku': 'SKU-$id',
          'status': status,
          'date_modified': '2026-04-01T10:00:00',
          'date_created': '2026-03-01T08:00:00',
          'categories': [
            {'id': 1, 'name': 'Test Category', 'slug': 'test-category'},
          ],
          'images': [
            {'id': 1, 'src': 'https://example.com/img$id.jpg'},
          ],
        };
      }

      test('sends PUT with minimal status-only payload and returns mapped ProductDraft', () async {
        Map<String, dynamic>? capturedBody;
        Uri? capturedUri;
        String? capturedMethod;

        final mockClient = MockClient((request) async {
          capturedMethod = request.method;
          capturedUri = request.url;
          capturedBody = jsonDecode(request.body) as Map<String, dynamic>;

          return http.Response(jsonEncode(buildSingleProductJson(id: 7, status: 'publish')), 200);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);
        final result = await repository.updateProductStatus('7', PublishStatus.published);

        // Verify the HTTP request shape.
        expect(capturedMethod, 'PUT');
        expect(capturedUri!.path, '/wp-json/wc/v3/products/7');
        expect(capturedBody, {'status': 'publish'});

        // Verify the returned domain object is cleanly mapped.
        expect(result, isA<ProductDraft>());
        expect(result.id, '7');
        expect(result.name, 'Product 7');
        expect(result.status, PublishStatus.published);
      });

      test('maps PublishStatus.draft to WooCommerce "draft" in the request', () async {
        Map<String, dynamic>? capturedBody;

        final mockClient = MockClient((request) async {
          capturedBody = jsonDecode(request.body) as Map<String, dynamic>;
          return http.Response(jsonEncode(buildSingleProductJson(id: 3, status: 'draft')), 200);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);
        final result = await repository.updateProductStatus('3', PublishStatus.draft);

        expect(capturedBody, {'status': 'draft'});
        expect(result.status, PublishStatus.draft);
      });

      test('throws WooCommerceApiException on non-200 response', () async {
        final mockClient = MockClient((request) async {
          return http.Response('{"code":"rest_cannot_edit"}', 403);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);

        expect(
          () => repository.updateProductStatus('7', PublishStatus.published),
          throwsA(isA<WooCommerceApiException>()),
        );
      });

      test('returns ProductDraft, not raw WooCommerce JSON (package boundary)', () async {
        final mockClient = MockClient((request) async {
          final json = buildSingleProductJson(id: 5, status: 'publish');
          json['permalink'] = 'https://store.example.com/product/5';
          return http.Response(jsonEncode(json), 200);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);
        final result = await repository.updateProductStatus('5', PublishStatus.published);

        expect(result, isA<ProductDraft>());
        expect(result.id, '5');
        expect(result.status, PublishStatus.published);
      });

      test('throws ArgumentError for unsupported status values', () {
        final mockClient = MockClient((request) async {
          return http.Response('{}', 200);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);

        expect(
          () => repository.updateProductStatus('1', PublishStatus.pendingReview),
          throwsA(isA<ArgumentError>()),
        );
        expect(
          () => repository.updateProductStatus('1', PublishStatus.unpublished),
          throwsA(isA<ArgumentError>()),
        );
      });

      test('returns ProductDraft, not raw WooCommerce JSON (package boundary)', () async {
        final mockClient = MockClient((request) async {
          // WooCommerce echoes back many extra fields that should NOT leak.
          final json = buildSingleProductJson(id: 5, status: 'publish');
          json['permalink'] = 'https://store.example.com/product/5';
          json['meta_data'] = [
            {'id': 1, 'key': '_internal', 'value': 'secret'},
          ];
          return http.Response(jsonEncode(json), 200);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);
        final result = await repository.updateProductStatus('5', PublishStatus.published);

        // The result is a domain ProductDraft — no WooCommerce-specific
        // fields like permalink or meta_data are exposed.
        expect(result, isA<ProductDraft>());
        expect(result.id, '5');
        expect(result.status, PublishStatus.published);
      });
    });

    group('updateProductPrice', () {
      Map<String, dynamic> buildPriceProductJson({required int id, required String price}) {
        return {
          'id': id,
          'name': 'Product $id',
          'description': '<p>Description</p>',
          'price': price,
          'regular_price': price,
          'sku': 'SKU-$id',
          'status': 'publish',
          'date_modified': '2026-04-01T10:00:00',
          'date_created': '2026-03-01T08:00:00',
          'categories': [
            {'id': 1, 'name': 'Test Category', 'slug': 'test-category'},
          ],
          'images': [
            {'id': 1, 'src': 'https://example.com/img$id.jpg'},
          ],
        };
      }

      test('sends PUT with regular_price payload and returns mapped ProductDraft', () async {
        Map<String, dynamic>? capturedBody;
        Uri? capturedUri;
        String? capturedMethod;

        final mockClient = MockClient((request) async {
          capturedMethod = request.method;
          capturedUri = request.url;
          capturedBody = jsonDecode(request.body) as Map<String, dynamic>;
          return http.Response(jsonEncode(buildPriceProductJson(id: 4, price: '11.99')), 200);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);
        final result = await repository.updateProductPrice('4', 11.99);

        expect(capturedMethod, 'PUT');
        expect(capturedUri!.path, '/wp-json/wc/v3/products/4');
        expect(capturedBody, {'regular_price': '11.99'});

        expect(result, isA<ProductDraft>());
        expect(result.id, '4');
        expect(result.price, 11.99);
      });

      test('throws WooCommerceApiException on non-200 response', () async {
        final mockClient = MockClient((request) async {
          return http.Response('{"code":"rest_cannot_edit"}', 403);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);

        expect(
          () => repository.updateProductPrice('4', 11.99),
          throwsA(isA<WooCommerceApiException>()),
        );
      });
    });

    group('updateProductName', () {
      Map<String, dynamic> buildNameProductJson({required int id, required String name}) {
        return {
          'id': id,
          'name': name,
          'description': '<p>Description</p>',
          'price': '10.99',
          'sku': 'SKU-$id',
          'status': 'publish',
          'date_modified': '2026-04-01T10:00:00',
          'date_created': '2026-03-01T08:00:00',
          'categories': [
            {'id': 1, 'name': 'Test Category', 'slug': 'test-category'},
          ],
          'images': [
            {'id': 1, 'src': 'https://example.com/img$id.jpg'},
          ],
        };
      }

      test('sends PUT with name payload and returns mapped ProductDraft', () async {
        Map<String, dynamic>? capturedBody;
        Uri? capturedUri;
        String? capturedMethod;

        final mockClient = MockClient((request) async {
          capturedMethod = request.method;
          capturedUri = request.url;
          capturedBody = jsonDecode(request.body) as Map<String, dynamic>;
          return http.Response(
            jsonEncode(buildNameProductJson(id: 4, name: 'Deluxe Jar Gripper')),
            200,
          );
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);
        final result = await repository.updateProductName('4', 'Deluxe Jar Gripper');

        expect(capturedMethod, 'PUT');
        expect(capturedUri!.path, '/wp-json/wc/v3/products/4');
        expect(capturedBody, {'name': 'Deluxe Jar Gripper'});

        expect(result, isA<ProductDraft>());
        expect(result.id, '4');
        expect(result.name, 'Deluxe Jar Gripper');
      });

      test('throws WooCommerceApiException on non-200 response', () async {
        final mockClient = MockClient((request) async {
          return http.Response('{"code":"rest_cannot_edit"}', 403);
        });

        final apiClient = WooCommerceApiClient(
          siteUrl: 'https://store.example.com',
          consumerKey: 'ck_test',
          consumerSecret: 'cs_test',
          httpClient: mockClient,
        );

        final repository = WordPressProductPublishingRepository(apiClient: apiClient);

        expect(
          () => repository.updateProductName('4', 'New Name'),
          throwsA(isA<WooCommerceApiException>()),
        );
      });
    });
  });
}
