import 'dart:async';
import 'dart:collection';

import 'package:flutter/foundation.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/network/failure.dart';
import 'package:superport_v2/features/approvals/domain/entities/approval_draft.dart';
import 'package:superport_v2/features/approvals/domain/usecases/get_approval_draft_use_case.dart';
import 'package:superport_v2/features/approvals/domain/usecases/list_approval_drafts_use_case.dart';
import 'package:superport_v2/features/approvals/domain/usecases/save_approval_draft_use_case.dart';
import 'package:superport_v2/features/inventory/inbound/presentation/models/inbound_record.dart';
import 'package:superport_v2/features/inventory/lookups/domain/entities/lookup_item.dart';
import 'package:superport_v2/features/inventory/lookups/domain/repositories/inventory_lookup_repository.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/transactions/presentation/services/transaction_detail_sync_service.dart';

/// 입고 목록 데이터를 가져오고 상태를 노출하는 컨트롤러.
class InboundController extends ChangeNotifier {
  InboundController({
    required StockTransactionRepository transactionRepository,
    required TransactionLineRepository lineRepository,
    required TransactionCustomerRepository customerRepository,
    required InventoryLookupRepository lookupRepository,
    List<String> fallbackStatusOptions = const ['작성중', '승인대기', '승인완료'],
    List<String> transactionTypeKeywords = const ['입고', 'inbound'],
    SaveApprovalDraftUseCase? saveDraftUseCase,
    GetApprovalDraftUseCase? getDraftUseCase,
    ListApprovalDraftsUseCase? listDraftsUseCase,
  }) : _transactionRepository = transactionRepository,
       _lineRepository = lineRepository,
       _customerRepository = customerRepository,
       _lookupRepository = lookupRepository,
       _saveDraftUseCase = saveDraftUseCase,
       _getDraftUseCase = getDraftUseCase,
       _listDraftsUseCase = listDraftsUseCase,
       _fallbackStatusOptions = List<String>.unmodifiable(
         fallbackStatusOptions,
       ),
       _transactionTypeKeywords = List<String>.unmodifiable(
         transactionTypeKeywords,
       ) {
    _statusOptions = List<String>.from(_fallbackStatusOptions);
  }

  final StockTransactionRepository _transactionRepository;
  final TransactionLineRepository _lineRepository;
  final TransactionCustomerRepository _customerRepository;
  final InventoryLookupRepository _lookupRepository;
  final SaveApprovalDraftUseCase? _saveDraftUseCase;
  final GetApprovalDraftUseCase? _getDraftUseCase;
  final ListApprovalDraftsUseCase? _listDraftsUseCase;
  final List<String> _fallbackStatusOptions;
  final List<String> _transactionTypeKeywords;

  late List<String> _statusOptions;
  final Map<String, LookupItem> _statusLookup = {};
  LookupItem? _transactionType;
  PaginatedResult<StockTransaction>? _result;
  List<InboundRecord> _records = const [];
  bool _isLoading = false;
  String? _errorMessage;
  StockTransactionListFilter? _lastFilter;
  final Set<int> _processingTransactionIds = <int>{};
  List<LookupItem> _approvalStatuses = const [];
  LookupItem? _defaultApprovalStatus;
  StockTransactionApprovalInput? _approvalDraft;

  UnmodifiableListView<String> get statusOptions =>
      UnmodifiableListView(_statusOptions);

  UnmodifiableMapView<String, LookupItem> get statusLookup =>
      UnmodifiableMapView(_statusLookup);

  LookupItem? get transactionType => _transactionType;
  LookupItem? get defaultApprovalStatus => _defaultApprovalStatus;
  UnmodifiableListView<LookupItem> get approvalStatuses =>
      UnmodifiableListView(_approvalStatuses);

  PaginatedResult<StockTransaction>? get result => _result;

  UnmodifiableListView<InboundRecord> get records =>
      UnmodifiableListView(_records);

  bool get isLoading => _isLoading;

  String? get errorMessage => _errorMessage;

  List<String> get fallbackStatusOptions => _fallbackStatusOptions;

  StockTransactionListFilter? get lastFilter => _lastFilter;

  UnmodifiableSetView<int> get processingTransactionIds =>
      UnmodifiableSetView(_processingTransactionIds);
  StockTransactionApprovalInput? get approvalDraft => _approvalDraft;

  /// 트랜잭션 상태 목록을 서버에서 읽어온다.
  Future<void> loadStatusOptions() async {
    try {
      final items = await _lookupRepository.fetchTransactionStatuses();
      final normalized = items
          .map((item) => item.name.trim())
          .where((name) => name.isNotEmpty)
          .toSet()
          .toList(growable: false);
      if (normalized.isEmpty) {
        return;
      }
      _statusOptions = normalized;
      _statusLookup
        ..clear()
        ..addEntries(
          items
              .where((item) => item.name.trim().isNotEmpty)
              .map((item) => MapEntry(item.name.trim(), item)),
        );
      notifyListeners();
    } catch (_) {
      // 실패 시 폴백 값을 유지한다.
    }
  }

  /// 결재 상태 목록을 조회한다.
  Future<void> loadApprovalStatuses() async {
    try {
      final items = await _lookupRepository.fetchApprovalStatuses();
      if (items.isEmpty) {
        return;
      }
      _approvalStatuses = items;
      _defaultApprovalStatus = _resolveDefaultApprovalStatus(items);
      notifyListeners();
    } catch (_) {
      // 오류 시 기존 상태 유지.
    }
  }

  /// 입고 트랜잭션 타입을 조회한다.
  Future<bool> resolveTransactionType() async {
    if (_transactionType != null) {
      return true;
    }
    try {
      final items = await _lookupRepository.fetchTransactionTypes();
      final matched = _matchLookup(items, _transactionTypeKeywords);
      if (matched == null) {
        return false;
      }
      _transactionType = matched;
      notifyListeners();
      return true;
    } catch (_) {
      return false;
    }
  }

  /// 결재 구성 초안 상태를 갱신한다.
  void updateApprovalDraft(StockTransactionApprovalInput approval) {
    _approvalDraft = approval;
    notifyListeners();
    _persistApprovalDraft(approval);
  }

  /// 결재 구성 초안을 초기화한다.
  void clearApprovalDraft() {
    if (_approvalDraft == null) {
      return;
    }
    _approvalDraft = null;
    notifyListeners();
  }

  Future<void> loadApprovalDraftFromServer({
    required int requesterId,
    int? transactionId,
  }) async {
    final listUseCase = _listDraftsUseCase;
    final getUseCase = _getDraftUseCase;
    if (listUseCase == null || getUseCase == null) {
      return;
    }
    try {
      final result = await listUseCase.call(
        ApprovalDraftListFilter(
          requesterId: requesterId,
          transactionId: transactionId,
          pageSize: 10,
        ),
      );
      if (result.items.isEmpty) {
        return;
      }
      final sessionKey = _draftSessionKey(
        requesterId,
        transactionId: transactionId,
      );
      final summary = result.items.firstWhere(
        (item) => item.sessionKey == sessionKey,
        orElse: () => result.items.first,
      );
      final detail = await getUseCase.call(
        id: summary.id,
        requesterId: requesterId,
      );
      if (detail == null) {
        return;
      }
      _approvalDraft = _toTransactionApproval(detail);
      notifyListeners();
    } catch (error, stackTrace) {
      debugPrint('[InboundController] 초안 복구 실패: $error\n$stackTrace');
    }
  }

  /// 필터에 맞는 입고 트랜잭션 목록을 조회한다.
  Future<void> fetchTransactions({
    required StockTransactionListFilter filter,
  }) async {
    _isLoading = true;
    _errorMessage = null;
    notifyListeners();

    try {
      _lastFilter = filter;
      final result = await _transactionRepository.list(filter: filter);
      _result = result;
      _records = result.items
          .map(InboundRecord.fromTransaction)
          .toList(growable: false);
    } catch (error) {
      _records = const [];
      _result = PaginatedResult<StockTransaction>(
        items: const [],
        page: filter.page,
        pageSize: filter.pageSize,
        total: 0,
      );
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  /// 마지막으로 사용한 필터를 기준으로 목록을 새로고침한다.
  Future<void> refresh({StockTransactionListFilter? filter}) async {
    final target = filter ?? _lastFilter;
    if (target == null) {
      return;
    }
    await fetchTransactions(filter: target);
  }

  /// 단일 입고 트랜잭션 상세 정보를 조회한다.
  Future<InboundRecord?> fetchTransactionDetail(
    int id, {
    List<String> include = const ['lines', 'customers', 'approval'],
  }) async {
    try {
      final transaction = await _transactionRepository.fetchDetail(
        id,
        include: include,
      );
      return InboundRecord.fromTransaction(transaction);
    } catch (error, stackTrace) {
      debugPrint('[InboundController] 상세 조회 실패(id=$id): $error');
      debugPrintStack(stackTrace: stackTrace);
      return null;
    }
  }

  void _persistApprovalDraft(StockTransactionApprovalInput approval) {
    final useCase = _saveDraftUseCase;
    if (useCase == null) {
      return;
    }
    if (approval.steps.isEmpty) {
      return;
    }
    final input = _buildApprovalDraftSaveInput(approval);
    if (!input.hasSteps) {
      return;
    }
    unawaited(
      Future<void>(() async {
        try {
          await useCase.call(input);
        } catch (error, stackTrace) {
          debugPrint('[InboundController] 초안 저장 실패: $error\n$stackTrace');
        }
      }),
    );
  }

  ApprovalDraftSaveInput _buildApprovalDraftSaveInput(
    StockTransactionApprovalInput approval,
  ) {
    final steps = approval.steps
        .map(
          (step) => ApprovalDraftStep(
            stepOrder: step.stepOrder,
            approverId: step.approverId,
            note: step.note,
          ),
        )
        .toList(growable: false);
    return ApprovalDraftSaveInput(
      requesterId: approval.requestedById,
      transactionId: null,
      templateId: approval.templateId,
      title: approval.title,
      summary: approval.summary,
      note: approval.note,
      metadata: approval.metadata,
      sessionKey: _draftSessionKey(approval.requestedById),
      statusId: approval.approvalStatusId,
      steps: steps,
    );
  }

  String _draftSessionKey(int requesterId, {int? transactionId}) {
    final base = 'inventory_inbound_$requesterId';
    if (transactionId == null) {
      return base;
    }
    return '${base}_$transactionId';
  }

  StockTransactionApprovalInput _toTransactionApproval(
    ApprovalDraftDetail detail,
  ) {
    final submission = detail.toSubmissionInput(defaultStatusId: null);
    return StockTransactionApprovalInput(
      requestedById: submission.requesterId,
      approvalStatusId: submission.statusId == 0 ? null : submission.statusId,
      templateId: submission.templateId,
      finalApproverId: submission.finalApproverId,
      requestedAt: submission.requestedAt,
      decidedAt: submission.decidedAt,
      cancelledAt: submission.cancelledAt,
      lastActionAt: submission.lastActionAt,
      title: submission.title,
      summary: submission.summary,
      note: submission.note,
      metadata: submission.metadata,
      steps: submission.steps,
    );
  }

  LookupItem? _resolveDefaultApprovalStatus(List<LookupItem> items) {
    for (final item in items) {
      if (item.isDefault) {
        return item;
      }
    }
    return items.isEmpty ? null : items.first;
  }

  /// 재고 트랜잭션을 생성하고 필요 시 목록을 갱신한다.
  Future<InboundRecord> createTransaction(
    StockTransactionCreateInput input, {
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) {
    _approvalDraft = input.approval;
    return _executeMutation(
      () => _transactionRepository.create(input),
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  /// 재고 트랜잭션을 수정한다.
  Future<InboundRecord> updateTransaction(
    int id,
    StockTransactionUpdateInput input, {
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) {
    return _executeMutation(
      () => _transactionRepository.update(id, input),
      trackedId: id,
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  /// 재고 트랜잭션을 삭제한다.
  Future<void> deleteTransaction(
    int id, {
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) async {
    await _executeVoidMutation(
      () => _transactionRepository.delete(id),
      trackedId: id,
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  /// 삭제된 재고 트랜잭션을 복구한다.
  Future<InboundRecord> restoreTransaction(
    int id, {
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) {
    return _executeMutation(
      () => _transactionRepository.restore(id),
      trackedId: id,
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  /// 재고 트랜잭션을 상신(submit)한다.
  Future<InboundRecord> submitTransaction(
    int id, {
    String? note,
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) {
    return _executeMutation(
      () => _transactionRepository.submit(id, note: note),
      trackedId: id,
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  /// 재고 트랜잭션을 완료 처리한다.
  Future<InboundRecord> completeTransaction(
    int id, {
    String? note,
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) {
    return _executeMutation(
      () => _transactionRepository.complete(id, note: note),
      trackedId: id,
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  /// 재고 트랜잭션을 승인 처리한다.
  Future<InboundRecord> approveTransaction(
    int id, {
    String? note,
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) {
    return _executeMutation(
      () => _transactionRepository.approve(id, note: note),
      trackedId: id,
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  /// 재고 트랜잭션을 반려 처리한다.
  Future<InboundRecord> rejectTransaction(
    int id, {
    String? note,
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) {
    return _executeMutation(
      () => _transactionRepository.reject(id, note: note),
      trackedId: id,
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  /// 재고 트랜잭션을 취소 처리한다.
  Future<InboundRecord> cancelTransaction(
    int id, {
    String? note,
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) {
    return _executeMutation(
      () => _transactionRepository.cancel(id, note: note),
      trackedId: id,
      refreshAfter: refreshAfter,
      refreshFilter: refreshFilter,
    );
  }

  Future<InboundRecord> _executeMutation(
    Future<StockTransaction> Function() mutation, {
    int? trackedId,
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) async {
    if (trackedId != null) {
      _processingTransactionIds.add(trackedId);
      notifyListeners();
    }
    _errorMessage = null;
    try {
      final transaction = await mutation();
      if (refreshAfter) {
        await _refreshAfterMutation(refreshFilter);
      } else {
        _upsertRecord(transaction);
      }
      return InboundRecord.fromTransaction(transaction);
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      rethrow;
    } finally {
      if (trackedId != null) {
        _processingTransactionIds.remove(trackedId);
        notifyListeners();
      }
    }
  }

  Future<void> _executeVoidMutation(
    Future<void> Function() mutation, {
    required int trackedId,
    bool refreshAfter = true,
    StockTransactionListFilter? refreshFilter,
  }) async {
    _processingTransactionIds.add(trackedId);
    notifyListeners();
    _errorMessage = null;
    try {
      await mutation();
      if (refreshAfter) {
        await _refreshAfterMutation(refreshFilter);
      } else {
        _removeRecord(trackedId);
      }
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      rethrow;
    } finally {
      _processingTransactionIds.remove(trackedId);
      notifyListeners();
    }
  }

  Future<void> _refreshAfterMutation(
    StockTransactionListFilter? refreshFilter,
  ) async {
    final filter = refreshFilter ?? _lastFilter;
    if (filter == null) {
      return;
    }
    await fetchTransactions(filter: filter);
  }

  void _upsertRecord(StockTransaction transaction) {
    final record = InboundRecord.fromTransaction(transaction);
    final updatedRecords = _records.toList();
    final transactionId = transaction.id;
    if (transactionId != null) {
      final index = updatedRecords.indexWhere(
        (item) => item.id == transactionId,
      );
      if (index >= 0) {
        updatedRecords[index] = record;
      } else {
        updatedRecords.insert(0, record);
      }
    } else {
      updatedRecords.insert(0, record);
    }
    _records = updatedRecords;

    final currentResult = _result;
    if (currentResult != null) {
      final transactions = currentResult.items.toList();
      if (transactionId != null) {
        final index = transactions.indexWhere(
          (item) => item.id == transactionId,
        );
        if (index >= 0) {
          transactions[index] = transaction;
        } else {
          transactions.insert(0, transaction);
        }
      } else {
        transactions.insert(0, transaction);
      }
      _result = currentResult.copyWith(items: transactions);
    }
    notifyListeners();
  }

  void _removeRecord(int transactionId) {
    final updatedRecords = _records.toList()
      ..removeWhere((record) => record.id == transactionId);
    _records = updatedRecords;

    final currentResult = _result;
    if (currentResult != null) {
      final transactions = currentResult.items
          .where((item) => item.id != transactionId)
          .toList();
      _result = currentResult.copyWith(items: transactions);
    }
    notifyListeners();
  }

  LookupItem? _matchLookup(List<LookupItem> items, List<String> keywords) {
    if (items.isEmpty) {
      return null;
    }
    final normalizedKeywords = keywords
        .map((keyword) => keyword.toLowerCase())
        .toList(growable: false);
    LookupItem? fallback;
    for (final item in items) {
      final name = item.name.toLowerCase();
      final code = item.code?.toLowerCase();
      for (final keyword in normalizedKeywords) {
        if (name == keyword || name.contains(keyword)) {
          return item;
        }
        if (code != null && (code == keyword || code.contains(keyword))) {
          return item;
        }
      }
      if (fallback == null && item.isDefault) {
        fallback = item;
      }
    }
    return fallback ?? items.first;
  }

  /// 라인 추가/수정/삭제 계획을 순차적으로 실행한다.
  Future<void> syncTransactionLines(
    int transactionId,
    TransactionLineSyncPlan plan,
  ) async {
    if (!plan.hasChanges) {
      return;
    }
    if (plan.updatedLines.isNotEmpty) {
      await _lineRepository.updateLines(transactionId, plan.updatedLines);
    }
    for (final lineId in plan.deletedLineIds) {
      await _lineRepository.deleteLine(lineId);
    }
    if (plan.createdLines.isNotEmpty) {
      await _lineRepository.addLines(transactionId, plan.createdLines);
    }
  }

  /// 고객 연결 변경 계획을 실행하여 파트너사 정보를 동기화한다.
  Future<void> syncTransactionCustomers(
    int transactionId,
    TransactionCustomerSyncPlan plan,
  ) async {
    if (!plan.hasChanges) {
      return;
    }
    if (plan.updatedCustomers.isNotEmpty) {
      await _customerRepository.updateCustomers(
        transactionId,
        plan.updatedCustomers,
      );
    }
    for (final linkId in plan.deletedCustomerIds) {
      await _customerRepository.deleteCustomer(linkId);
    }
    if (plan.createdCustomers.isNotEmpty) {
      await _customerRepository.addCustomers(
        transactionId,
        plan.createdCustomers,
      );
    }
  }
}
