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';

/// 라인 편집 폼에서 수집한 입력 값을 정규화한 모델.
class TransactionLineDraft {
  TransactionLineDraft({
    this.id,
    required this.lineNo,
    required this.productId,
    required this.quantity,
    required this.unitPrice,
    this.note,
  });

  /// 기존 라인의 식별자. 신규 라인은 `null`이다.
  final int? id;

  /// 서버에 전달할 라인 순번.
  final int lineNo;

  /// 선택된 제품의 식별자.
  final int productId;

  /// 수량.
  final int quantity;

  /// 단가.
  final double unitPrice;

  /// 비고. 공백일 경우 `null`로 정규화한다.
  final String? note;
}

/// 라인 변경 사항을 서버 호출 단위로 정리한 결과.
class TransactionLineSyncPlan {
  TransactionLineSyncPlan({
    this.createdLines = const [],
    this.updatedLines = const [],
    this.deletedLineIds = const [],
  });

  /// 새로 추가해야 할 라인 목록.
  final List<TransactionLineCreateInput> createdLines;

  /// 수정이 필요한 기존 라인 목록.
  final List<TransactionLineUpdateInput> updatedLines;

  /// 삭제해야 할 라인 ID 목록.
  final List<int> deletedLineIds;

  /// 변경 사항이 있는지 여부.
  bool get hasChanges =>
      createdLines.isNotEmpty ||
      updatedLines.isNotEmpty ||
      deletedLineIds.isNotEmpty;
}

/// 고객 연결 입력 값을 정규화한 모델.
class TransactionCustomerDraft {
  TransactionCustomerDraft({this.id, required this.customerId, this.note});

  /// 기존 고객 연결 ID. 신규 연결은 `null`이다.
  final int? id;

  /// 선택된 고객 ID.
  final int customerId;

  /// 비고. 공백일 경우 `null`.
  final String? note;
}

/// 고객 연결 변경 사항을 서버 호출 단위로 정리한 결과.
class TransactionCustomerSyncPlan {
  TransactionCustomerSyncPlan({
    this.createdCustomers = const [],
    this.updatedCustomers = const [],
    this.deletedCustomerIds = const [],
  });

  /// 새로 추가할 고객 연결 입력.
  final List<TransactionCustomerCreateInput> createdCustomers;

  /// 비고 등 속성이 변경된 고객 연결 입력.
  final List<TransactionCustomerUpdateInput> updatedCustomers;

  /// 삭제 대상 고객 연결 ID.
  final List<int> deletedCustomerIds;

  /// 변경 사항이 있는지 여부.
  bool get hasChanges =>
      createdCustomers.isNotEmpty ||
      updatedCustomers.isNotEmpty ||
      deletedCustomerIds.isNotEmpty;
}

/// 재고 트랜잭션 라인·고객 입력을 기존 데이터와 비교해 동기화 계획을 산출하는 유틸리티.
class TransactionDetailSyncService {
  const TransactionDetailSyncService();

  /// 현재 서버 상태와 폼 입력을 비교해 라인 변경 계획을 생성한다.
  TransactionLineSyncPlan buildLinePlan({
    required List<TransactionLineDraft> drafts,
    required List<StockTransactionLine> currentLines,
  }) {
    final createdLines = <TransactionLineCreateInput>[];
    final updatedLines = <TransactionLineUpdateInput>[];
    final deletedLineIds = <int>{};

    final currentById = <int, StockTransactionLine>{};
    for (final line in currentLines) {
      final id = line.id;
      if (id != null) {
        currentById[id] = line;
      }
    }
    final remainingIds = currentById.keys.toSet();

    for (final draft in drafts) {
      final draftId = draft.id;
      final normalizedNote = _normalizeOptionalText(draft.note);
      if (draftId == null) {
        createdLines.add(
          TransactionLineCreateInput(
            lineNo: draft.lineNo,
            productId: draft.productId,
            quantity: draft.quantity,
            unitPrice: draft.unitPrice,
            note: normalizedNote,
          ),
        );
        continue;
      }

      final current = currentById[draftId];
      if (current == null) {
        createdLines.add(
          TransactionLineCreateInput(
            lineNo: draft.lineNo,
            productId: draft.productId,
            quantity: draft.quantity,
            unitPrice: draft.unitPrice,
            note: normalizedNote,
          ),
        );
        continue;
      }

      remainingIds.remove(draftId);

      if (current.product.id != draft.productId) {
        deletedLineIds.add(draftId);
        createdLines.add(
          TransactionLineCreateInput(
            lineNo: draft.lineNo,
            productId: draft.productId,
            quantity: draft.quantity,
            unitPrice: draft.unitPrice,
            note: normalizedNote,
          ),
        );
        continue;
      }

      int? nextLineNo;
      if (current.lineNo != draft.lineNo) {
        nextLineNo = draft.lineNo;
      }

      int? nextQuantity;
      if (current.quantity != draft.quantity) {
        nextQuantity = draft.quantity;
      }

      double? nextUnitPrice;
      if ((current.unitPrice - draft.unitPrice).abs() > 0.0001) {
        nextUnitPrice = draft.unitPrice;
      }

      final currentNote = _normalizeOptionalText(current.note);
      String? nextNote;
      if (currentNote != normalizedNote) {
        nextNote = normalizedNote;
      }

      if (nextLineNo != null ||
          nextQuantity != null ||
          nextUnitPrice != null ||
          nextNote != null) {
        updatedLines.add(
          TransactionLineUpdateInput(
            id: draftId,
            lineNo: nextLineNo,
            quantity: nextQuantity,
            unitPrice: nextUnitPrice,
            note: nextNote,
          ),
        );
      }
    }

    for (final id in remainingIds) {
      deletedLineIds.add(id);
    }

    return TransactionLineSyncPlan(
      createdLines: createdLines,
      updatedLines: updatedLines,
      deletedLineIds: deletedLineIds.toList(),
    );
  }

  /// 현재 서버 상태와 폼 입력을 비교해 고객 연결 변경 계획을 생성한다.
  TransactionCustomerSyncPlan buildCustomerPlan({
    required List<TransactionCustomerDraft> drafts,
    required List<StockTransactionCustomer> currentCustomers,
  }) {
    final createdCustomers = <TransactionCustomerCreateInput>[];
    final updatedCustomers = <TransactionCustomerUpdateInput>[];
    final deletedCustomerIds = <int>{};

    final currentById = <int, StockTransactionCustomer>{};
    final currentByCustomerId = <int, StockTransactionCustomer>{};

    for (final customer in currentCustomers) {
      final id = customer.id;
      if (id != null) {
        currentById[id] = customer;
      }
      currentByCustomerId[customer.customer.id] = customer;
    }
    final remainingIds = currentById.keys.toSet();

    for (final draft in drafts) {
      final draftId = draft.id;
      final normalizedNote = _normalizeOptionalText(draft.note);
      if (draftId != null) {
        final currentByLink = currentById[draftId];
        if (currentByLink != null) {
          remainingIds.remove(draftId);
          final currentNote = _normalizeOptionalText(currentByLink.note);
          if (currentNote != normalizedNote) {
            updatedCustomers.add(
              TransactionCustomerUpdateInput(id: draftId, note: normalizedNote),
            );
          }
          continue;
        }
      }

      final fallback = currentByCustomerId[draft.customerId];
      if (fallback != null && fallback.id != null) {
        final fallbackId = fallback.id!;
        remainingIds.remove(fallbackId);
        final currentNote = _normalizeOptionalText(fallback.note);
        if (currentNote != normalizedNote) {
          updatedCustomers.add(
            TransactionCustomerUpdateInput(
              id: fallbackId,
              note: normalizedNote,
            ),
          );
        }
        continue;
      }

      createdCustomers.add(
        TransactionCustomerCreateInput(
          customerId: draft.customerId,
          note: normalizedNote,
        ),
      );
    }

    for (final id in remainingIds) {
      deletedCustomerIds.add(id);
    }

    return TransactionCustomerSyncPlan(
      createdCustomers: createdCustomers,
      updatedCustomers: updatedCustomers,
      deletedCustomerIds: deletedCustomerIds.toList(),
    );
  }
}

String? _normalizeOptionalText(String? value) {
  if (value == null) {
    return null;
  }
  final trimmed = value.trim();
  if (trimmed.isEmpty) {
    return null;
  }
  return trimmed;
}
