import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/features/masters/product/domain/entities/product.dart';
import 'package:superport_v2/features/masters/product/domain/repositories/product_repository.dart';
import 'package:superport_v2/features/masters/product/presentation/controllers/product_controller.dart';
import 'package:superport_v2/features/masters/uom/domain/entities/uom.dart';
import 'package:superport_v2/features/masters/uom/domain/repositories/uom_repository.dart';
import 'package:superport_v2/features/masters/vendor/domain/entities/vendor.dart';
import 'package:superport_v2/features/masters/vendor/domain/repositories/vendor_repository.dart';

class _MockProductRepository extends Mock implements ProductRepository {}

class _MockVendorRepository extends Mock implements VendorRepository {}

class _MockUomRepository extends Mock implements UomRepository {}

class _FakeProductInput extends Fake implements ProductInput {}

void main() {
  late ProductController controller;
  late _MockProductRepository productRepository;
  late _MockVendorRepository vendorRepository;
  late _MockUomRepository uomRepository;

  final sampleProduct = Product(
    id: 1,
    productCode: 'P-001',
    productName: '테스트 제품',
    vendor: ProductVendor(id: 10, vendorCode: 'V-10', vendorName: '벤더'),
    uom: ProductUom(id: 5, uomName: 'EA'),
  );

  PaginatedResult<Product> createResult({List<Product>? items}) {
    final list = items ?? [sampleProduct];
    return PaginatedResult<Product>(
      items: list,
      page: 1,
      pageSize: 20,
      total: list.length,
    );
  }

  PaginatedResult<Vendor> createVendorResult() {
    return PaginatedResult<Vendor>(
      items: [Vendor(id: 10, vendorCode: 'V-10', vendorName: '벤더')],
      page: 1,
      pageSize: 20,
      total: 1,
    );
  }

  PaginatedResult<Uom> createUomResult() {
    return PaginatedResult<Uom>(
      items: [Uom(id: 5, uomName: 'EA')],
      page: 1,
      pageSize: 20,
      total: 1,
    );
  }

  setUpAll(() {
    registerFallbackValue(_FakeProductInput());
  });

  setUp(() {
    productRepository = _MockProductRepository();
    vendorRepository = _MockVendorRepository();
    uomRepository = _MockUomRepository();
    controller = ProductController(
      productRepository: productRepository,
      vendorRepository: vendorRepository,
      uomRepository: uomRepository,
    );
  });

  group('fetch', () {
    test('정상 조회 시 결과를 저장한다', () async {
      when(
        () => productRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          vendorId: any(named: 'vendorId'),
          uomId: any(named: 'uomId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer((_) async => createResult());

      await controller.fetch();

      expect(controller.result?.items, isNotEmpty);
      verify(
        () => productRepository.list(
          page: 1,
          pageSize: 20,
          query: null,
          vendorId: null,
          uomId: null,
          isActive: null,
        ),
      ).called(1);
    });

    test('에러 발생 시 errorMessage가 설정된다', () async {
      when(
        () => productRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          vendorId: any(named: 'vendorId'),
          uomId: any(named: 'uomId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenThrow(Exception('fail'));

      await controller.fetch();

      expect(controller.errorMessage, isNotNull);
    });
  });

  test('loadLookups 호출 시 벤더/단위 목록을 설정한다', () async {
    when(
      () => vendorRepository.list(
        page: any(named: 'page'),
        pageSize: any(named: 'pageSize'),
        query: any(named: 'query'),
        isActive: any(named: 'isActive'),
      ),
    ).thenAnswer((_) async => createVendorResult());
    when(
      () => uomRepository.list(
        page: any(named: 'page'),
        pageSize: any(named: 'pageSize'),
        query: any(named: 'query'),
        isActive: any(named: 'isActive'),
      ),
    ).thenAnswer((_) async => createUomResult());

    await controller.loadLookups();

    expect(controller.vendorOptions, isNotEmpty);
    expect(controller.uomOptions, isNotEmpty);
  });

  test('쿼리/필터 업데이트 시 상태 반영', () {
    controller.updateQuery('abc');
    controller.updateVendorFilter(1);
    controller.updateUomFilter(2);
    controller.updateStatusFilter(ProductStatusFilter.activeOnly);

    expect(controller.query, 'abc');
    expect(controller.vendorFilter, 1);
    expect(controller.uomFilter, 2);
    expect(controller.statusFilter, ProductStatusFilter.activeOnly);
  });

  group('mutations', () {
    setUp(() {
      when(
        () => productRepository.list(
          page: any(named: 'page'),
          pageSize: any(named: 'pageSize'),
          query: any(named: 'query'),
          vendorId: any(named: 'vendorId'),
          uomId: any(named: 'uomId'),
          isActive: any(named: 'isActive'),
        ),
      ).thenAnswer((_) async => createResult());
    });

    final input = ProductInput(
      productCode: 'P-001',
      productName: '테스트 제품',
      vendorId: 10,
      uomId: 5,
    );

    test('create 성공 시 목록을 갱신한다', () async {
      when(
        () => productRepository.create(any()),
      ).thenAnswer((_) async => sampleProduct);

      final created = await controller.create(input);

      expect(created, isNotNull);
      verify(() => productRepository.create(any())).called(1);
    });

    test('update 성공 시 현재 페이지 유지하며 갱신한다', () async {
      when(
        () => productRepository.update(any(), any()),
      ).thenAnswer((_) async => sampleProduct);

      final updated = await controller.update(1, input);

      expect(updated, isNotNull);
      verify(() => productRepository.update(1, any())).called(1);
    });

    test('delete 성공 시 true 반환 및 갱신', () async {
      when(() => productRepository.delete(any())).thenAnswer((_) async {});

      final success = await controller.delete(1);

      expect(success, isTrue);
      verify(() => productRepository.delete(1)).called(1);
    });

    test('restore 성공 시 갱신', () async {
      when(
        () => productRepository.restore(any()),
      ).thenAnswer((_) async => sampleProduct);

      final restored = await controller.restore(1);

      expect(restored, isNotNull);
      verify(() => productRepository.restore(1)).called(1);
    });
  });
}
