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/core/network/api_error.dart';
import 'package:superport_v2/core/network/failure.dart';
import 'package:superport_v2/features/inventory/rental/presentation/controllers/rental_controller.dart';
import 'package:superport_v2/features/inventory/transactions/domain/entities/stock_transaction.dart';
import 'package:superport_v2/features/inventory/transactions/domain/entities/stock_transaction_input.dart';
import 'package:superport_v2/features/inventory/transactions/domain/repositories/stock_transaction_repository.dart';
import 'package:superport_v2/features/inventory/lookups/domain/repositories/inventory_lookup_repository.dart';

class _MockStockTransactionRepository extends Mock
    implements StockTransactionRepository {}

class _MockInventoryLookupRepository extends Mock
    implements InventoryLookupRepository {}

class _MockTransactionLineRepository extends Mock
    implements TransactionLineRepository {}

class _MockTransactionCustomerRepository extends Mock
    implements TransactionCustomerRepository {}

void main() {
  group('RentalController', () {
    late StockTransactionRepository transactionRepository;
    late InventoryLookupRepository lookupRepository;
    late TransactionLineRepository lineRepository;
    late TransactionCustomerRepository customerRepository;
    late RentalController controller;

    setUpAll(() {
      registerFallbackValue(
        StockTransactionCreateInput(
          transactionTypeId: 0,
          transactionStatusId: 0,
          warehouseId: 0,
          transactionDate: DateTime(2000, 1, 1),
          createdById: 0,
          approval: StockTransactionApprovalInput(requestedById: 0),
        ),
      );
      registerFallbackValue(
        StockTransactionUpdateInput(transactionStatusId: 0),
      );
      registerFallbackValue(StockTransactionListFilter());
    });

    setUp(() {
      transactionRepository = _MockStockTransactionRepository();
      lookupRepository = _MockInventoryLookupRepository();
      lineRepository = _MockTransactionLineRepository();
      customerRepository = _MockTransactionCustomerRepository();
      controller = RentalController(
        transactionRepository: transactionRepository,
        lineRepository: lineRepository,
        customerRepository: customerRepository,
        lookupRepository: lookupRepository,
      );
    });

    test('createTransaction은 레코드를 추가한다', () async {
      final transaction = _buildRentalTransaction();
      when(
        () => transactionRepository.create(any()),
      ).thenAnswer((_) async => transaction);

      final record = await controller.createTransaction(
        StockTransactionCreateInput(
          transactionTypeId: 1,
          transactionStatusId: 2,
          warehouseId: 4,
          transactionDate: DateTime(2024, 5, 1),
          createdById: 5,
          approval: StockTransactionApprovalInput(requestedById: 5),
        ),
        refreshAfter: false,
      );

      expect(record.id, equals(transaction.id));
      expect(controller.records.first.id, equals(transaction.id));
      expect(controller.approvalDraft?.requestedById, equals(5));
    });

    test('updateApprovalDraft와 clearApprovalDraft가 초안을 관리한다', () {
      final approval = StockTransactionApprovalInput(
        requestedById: 44,
        approvalStatusId: 9,
        note: '대여 승인',
      );

      controller.updateApprovalDraft(approval);

      expect(controller.approvalDraft?.requestedById, equals(44));
      expect(controller.approvalDraft?.approvalStatusId, equals(9));

      controller.clearApprovalDraft();

      expect(controller.approvalDraft, isNull);
    });

    test('deleteTransaction은 레코드를 제거하고 처리 상태를 초기화한다', () async {
      final transaction = _buildRentalTransaction();
      when(
        () => transactionRepository.create(any()),
      ).thenAnswer((_) async => transaction);
      when(
        () => transactionRepository.delete(any()),
      ).thenAnswer((_) async => Future<void>.value());

      await controller.createTransaction(
        StockTransactionCreateInput(
          transactionTypeId: 1,
          transactionStatusId: 2,
          warehouseId: 4,
          transactionDate: DateTime(2024, 5, 1),
          createdById: 5,
          approval: StockTransactionApprovalInput(requestedById: 5),
        ),
        refreshAfter: false,
      );

      final future = controller.deleteTransaction(
        transaction.id!,
        refreshAfter: false,
      );

      expect(
        controller.processingTransactionIds.contains(transaction.id),
        isTrue,
      );

      await future;

      expect(controller.records, isEmpty);
      expect(
        controller.processingTransactionIds.contains(transaction.id),
        isFalse,
      );
    });

    test('completeTransaction은 마지막 필터 설정을 유지하며 새로고침한다', () async {
      final rental = _buildRentalTransaction();
      final other = _buildNonRentalTransaction();

      when(
        () => transactionRepository.list(filter: any(named: 'filter')),
      ).thenAnswer(
        (_) async => PaginatedResult<StockTransaction>(
          items: [rental, other],
          page: 1,
          pageSize: 20,
          total: 2,
        ),
      );

      await controller.fetchTransactions(
        filter: StockTransactionListFilter(transactionTypeId: 1),
        filterByRentalTypes: false,
      );

      final updated = rental.copyWith(
        status: StockTransactionStatus(id: 2, name: '완료'),
      );
      when(
        () => transactionRepository.complete(any(), note: any(named: 'note')),
      ).thenAnswer((_) async => updated);
      when(
        () => transactionRepository.list(filter: any(named: 'filter')),
      ).thenAnswer(
        (_) async => PaginatedResult<StockTransaction>(
          items: [updated, other],
          page: 1,
          pageSize: 20,
          total: 2,
        ),
      );

      final result = await controller.completeTransaction(rental.id!);

      expect(result.status, equals('완료'));
      expect(controller.records.length, equals(2));
    });

    test('fetchTransactions 실패 시 Failure 메시지를 저장한다', () async {
      final exception = ApiException(
        code: ApiErrorCode.conflict,
        message: '대여 목록을 가져오는 데 실패했습니다.',
        details: {
          'errors': {
            'status': ['상태 필터가 올바르지 않습니다.'],
          },
        },
      );

      when(
        () => transactionRepository.list(filter: any(named: 'filter')),
      ).thenThrow(exception);

      await controller.fetchTransactions(
        filter: StockTransactionListFilter(transactionTypeId: 1),
      );

      final failure = Failure.from(exception);
      expect(controller.errorMessage, equals(failure.describe()));
      expect(controller.records, isEmpty);
    });
  });
}

StockTransaction _buildRentalTransaction({
  int id = 300,
  String statusName = '대여중',
}) {
  return StockTransaction(
    id: id,
    transactionNo: 'RENT-$id',
    transactionDate: DateTime(2024, 5, 1),
    type: StockTransactionType(id: 30, name: '대여'),
    status: StockTransactionStatus(id: 31, name: statusName),
    warehouse: StockTransactionWarehouse(id: 5, code: 'WH-5', name: '대여 창고'),
    createdBy: StockTransactionEmployee(
      id: 5,
      employeeNo: 'EMP-5',
      name: '대여 담당',
    ),
    note: '렌탈',
    lines: [
      StockTransactionLine(
        id: 20,
        lineNo: 1,
        product: StockTransactionProduct(id: 20, code: 'P-20', name: '렌탈 품목'),
        quantity: 2,
        unitPrice: 15000.0,
      ),
    ],
    customers: [
      StockTransactionCustomer(
        id: 2,
        customer: StockTransactionCustomerSummary(
          id: 200,
          code: 'C-200',
          name: '렌탈 고객',
        ),
      ),
    ],
    expectedReturnDate: DateTime(2024, 5, 20),
  );
}

StockTransaction _buildNonRentalTransaction() {
  return StockTransaction(
    id: 301,
    transactionNo: 'SRV-301',
    transactionDate: DateTime(2024, 5, 2),
    type: StockTransactionType(id: 40, name: '수리'),
    status: StockTransactionStatus(id: 32, name: '진행중'),
    warehouse: StockTransactionWarehouse(id: 6, code: 'WH-6', name: '서비스 센터'),
    createdBy: StockTransactionEmployee(
      id: 6,
      employeeNo: 'EMP-6',
      name: '서비스 담당',
    ),
    lines: [
      StockTransactionLine(
        id: 21,
        lineNo: 1,
        product: StockTransactionProduct(id: 21, code: 'P-21', name: '서비스 품목'),
        quantity: 1,
        unitPrice: 5000.0,
      ),
    ],
    customers: const [],
  );
}
