import 'dart:convert';

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

// ── Helpers ────────────────────────────────────────────────────────────────

Map<String, dynamic> _orderJson({String id = '1001', String status = 'processing'}) {
  return {
    'id': int.parse(id),
    'status': status,
    'date_created': '2026-04-01T10:00:00',
    'customer_note': '',
    'payment_method_title': 'Credit Card',
    'billing': {
      'first_name': 'Jane',
      'last_name': 'Doe',
      'email': 'jane@example.com',
      'phone': '555-0100',
      'address_1': '123 Main St',
      'city': 'Asheville',
      'state': 'NC',
      'postcode': '28801',
      'country': 'US',
    },
    'shipping': {
      'first_name': 'Jane',
      'last_name': 'Doe',
      'address_1': '123 Main St',
      'city': 'Asheville',
      'state': 'NC',
      'postcode': '28801',
      'country': 'US',
    },
    'line_items': [
      {
        'name': 'Floral Bowl Cozy',
        'sku': 'BC-FLR-001',
        'quantity': 2,
        'price': '12.99',
        'product_id': 42,
        'variation_id': 0,
        'meta_data': [],
      },
    ],
    'meta_data': [],
  };
}

WooCommerceApiClient _clientWith(MockClient mockClient) {
  return WooCommerceApiClient(
    siteUrl: 'https://example.com',
    consumerKey: 'ck_test',
    consumerSecret: 'cs_test',
    httpClient: mockClient,
  );
}

WooCommerceOrdersRepository _repoWith(MockClient mockClient) {
  return WooCommerceOrdersRepository(apiClient: _clientWith(mockClient));
}

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

void main() {
  // ── WooCommerceApiClient — orders endpoints ────────────────────────────────

  group('WooCommerceApiClient — getOrders', () {
    test('returns list of order maps on 200', () async {
      final client = MockClient((request) async {
        expect(request.url.path, contains('/orders'));
        return http.Response(jsonEncode([_orderJson(id: '1'), _orderJson(id: '2')]), 200);
      });

      final apiClient = _clientWith(client);
      final orders = await apiClient.getOrders();
      expect(orders.length, 2);
      expect(orders.first['id'], 1);
    });

    test('throws WooCommerceApiException on non-200', () async {
      final client = MockClient((_) async => http.Response('{"message":"Unauthorized"}', 401));

      final apiClient = _clientWith(client);
      expect(() => apiClient.getOrders(), throwsA(isA<WooCommerceApiException>()));
    });
  });

  group('WooCommerceApiClient — getOrder', () {
    test('returns single order map on 200', () async {
      final client = MockClient((request) async {
        expect(request.url.path, contains('/orders/1001'));
        return http.Response(jsonEncode(_orderJson(id: '1001')), 200);
      });

      final apiClient = _clientWith(client);
      final order = await apiClient.getOrder('1001');
      expect(order['id'], 1001);
    });

    test('throws WooCommerceApiException on 404', () async {
      final client = MockClient((_) async => http.Response('{"message":"Not found"}', 404));

      final apiClient = _clientWith(client);
      expect(() => apiClient.getOrder('9999'), throwsA(isA<WooCommerceApiException>()));
    });
  });

  group('WooCommerceApiClient — updateOrderStatus', () {
    test('sends PUT with status body and returns updated order', () async {
      final client = MockClient((request) async {
        expect(request.method, 'PUT');
        expect(request.url.path, contains('/orders/1001'));
        final body = jsonDecode(request.body) as Map<String, dynamic>;
        expect(body['status'], 'completed');
        return http.Response(jsonEncode(_orderJson(id: '1001', status: 'completed')), 200);
      });

      final apiClient = _clientWith(client);
      final result = await apiClient.updateOrderStatus('1001', 'completed');
      expect(result['status'], 'completed');
    });

    test('throws WooCommerceApiException on non-200', () async {
      final client = MockClient((_) async => http.Response('{"message":"Error"}', 500));

      final apiClient = _clientWith(client);
      expect(
        () => apiClient.updateOrderStatus('1001', 'completed'),
        throwsA(isA<WooCommerceApiException>()),
      );
    });
  });

  group('WooCommerceApiClient — createOrder', () {
    test('sends POST and returns created order on 201', () async {
      final client = MockClient((request) async {
        expect(request.method, 'POST');
        expect(request.url.path, contains('/orders'));
        return http.Response(jsonEncode(_orderJson(id: '2000', status: 'pending')), 201);
      });

      final apiClient = _clientWith(client);
      final result = await apiClient.createOrder({'status': 'pending'});
      expect(result['id'], 2000);
    });

    test('throws WooCommerceApiException on non-201', () async {
      final client = MockClient((_) async => http.Response('{"message":"Error"}', 400));

      final apiClient = _clientWith(client);
      expect(
        () => apiClient.createOrder({'status': 'pending'}),
        throwsA(isA<WooCommerceApiException>()),
      );
    });
  });

  group('WooCommerceApiClient — addOrderNote', () {
    test('sends POST to notes endpoint and returns note on 201', () async {
      final client = MockClient((request) async {
        expect(request.method, 'POST');
        expect(request.url.path, contains('/orders/1001/notes'));
        final body = jsonDecode(request.body) as Map<String, dynamic>;
        expect(body['note'], 'Please gift wrap.');
        return http.Response(jsonEncode({'id': 1, 'note': 'Please gift wrap.'}), 201);
      });

      final apiClient = _clientWith(client);
      final result = await apiClient.addOrderNote('1001', 'Please gift wrap.');
      expect(result['note'], 'Please gift wrap.');
    });

    test('throws WooCommerceApiException on non-201', () async {
      final client = MockClient((_) async => http.Response('{"message":"Error"}', 400));

      final apiClient = _clientWith(client);
      expect(() => apiClient.addOrderNote('1001', 'Note'), throwsA(isA<WooCommerceApiException>()));
    });
  });

  // ── WooCommerceOrdersRepository ────────────────────────────────────────────

  group('WooCommerceOrdersRepository — getOrders', () {
    test('returns mapped orders from API', () async {
      final client = MockClient(
        (_) async => http.Response(jsonEncode([_orderJson(id: '1'), _orderJson(id: '2')]), 200),
      );

      final repo = _repoWith(client);
      final orders = await repo.getOrders();
      expect(orders.length, 2);
      expect(orders.first.id, '1');
    });
  });

  group('WooCommerceOrdersRepository — getOrder', () {
    test('returns mapped order when found', () async {
      final client = MockClient(
        (_) async => http.Response(jsonEncode(_orderJson(id: '1001')), 200),
      );

      final repo = _repoWith(client);
      final order = await repo.getOrder('1001');
      expect(order, isNotNull);
      expect(order!.id, '1001');
    });

    test('returns null on 404', () async {
      final client = MockClient((_) async => http.Response('{"message":"Not found"}', 404));

      final repo = _repoWith(client);
      final order = await repo.getOrder('9999');
      expect(order, isNull);
    });
  });

  group('WooCommerceOrdersRepository — updateOrderStatus', () {
    test('returns success result with updated order', () async {
      final client = MockClient(
        (_) async => http.Response(jsonEncode(_orderJson(id: '1001', status: 'completed')), 200),
      );

      final repo = _repoWith(client);
      final result = await repo.updateOrderStatus('1001', OrderStatus.delivered);
      expect(result.success, true);
      expect(result.updatedOrder!.id, '1001');
      expect(result.updatedOrder!.status, OrderStatus.delivered);
    });

    test('returns failure result on API error', () async {
      final client = MockClient((_) async => http.Response('{"message":"Error"}', 500));

      final repo = _repoWith(client);
      final result = await repo.updateOrderStatus('1001', OrderStatus.delivered);
      expect(result.success, false);
      expect(result.errorMessage, isNotNull);
    });
  });

  group('WooCommerceOrdersRepository — addOrderNote', () {
    test('returns failure for empty note without calling API', () async {
      var apiCalled = false;
      final client = MockClient((_) async {
        apiCalled = true;
        return http.Response('{}', 201);
      });

      final repo = _repoWith(client);
      final result = await repo.addOrderNote('1001', '   ');
      expect(result.success, false);
      expect(result.errorMessage, isNotNull);
      expect(apiCalled, false);
    });

    test('returns success result after adding note and fetching updated order', () async {
      var callCount = 0;
      final client = MockClient((request) async {
        callCount++;
        if (request.method == 'POST') {
          return http.Response(jsonEncode({'id': 1, 'note': 'Gift wrap please.'}), 201);
        }
        // GET order after note added
        return http.Response(jsonEncode(_orderJson(id: '1001')), 200);
      });

      final repo = _repoWith(client);
      final result = await repo.addOrderNote('1001', 'Gift wrap please.');
      expect(result.success, true);
      expect(callCount, 2); // POST note + GET order
    });
  });

  group('WooCommerceOrdersRepository — createOrder', () {
    test('returns success result with created order', () async {
      final client = MockClient(
        (_) async => http.Response(jsonEncode(_orderJson(id: '2000', status: 'pending')), 201),
      );

      final repo = _repoWith(client);
      final newOrder = Order(
        id: 'KC-NEW',
        customerName: 'New Customer',
        customerEmail: 'new@example.com',
        orderDate: DateTime(2026, 7, 1),
        status: OrderStatus.pending,
        items: const [],
        shippingAddress: '',
      );

      final result = await repo.createOrder(newOrder);
      expect(result.success, true);
      expect(result.updatedOrder!.id, '2000');
    });

    test('returns failure result on API error', () async {
      final client = MockClient((_) async => http.Response('{"message":"Error"}', 400));

      final repo = _repoWith(client);
      final newOrder = Order(
        id: 'KC-NEW',
        customerName: 'New Customer',
        customerEmail: 'new@example.com',
        orderDate: DateTime(2026, 7, 1),
        status: OrderStatus.pending,
        items: const [],
        shippingAddress: '',
      );

      final result = await repo.createOrder(newOrder);
      expect(result.success, false);
      expect(result.errorMessage, isNotNull);
    });
  });

  // ── FakeOrdersRepository — getOrder ───────────────────────────────────────

  group('FakeOrdersRepository — getOrder', () {
    late FakeOrdersRepository repository;

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

    test('returns order for known ID', () async {
      final order = await repository.getOrder('KC-1001');
      expect(order, isNotNull);
      expect(order!.id, 'KC-1001');
    });

    test('returns null for unknown ID', () async {
      final order = await repository.getOrder('UNKNOWN');
      expect(order, isNull);
    });

    test('returns updated order after status change', () async {
      await repository.updateOrderStatus('KC-1001', OrderStatus.processing);
      final order = await repository.getOrder('KC-1001');
      expect(order!.status, OrderStatus.processing);
    });
  });
}
