import 'package:flutter/foundation.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/network/failure.dart';

import '../../../domain/entities/approval.dart';
import '../../../domain/entities/approval_flow.dart';
import '../../../domain/repositories/approval_repository.dart';
import '../../../domain/usecases/recall_approval_use_case.dart';
import '../../../domain/usecases/resubmit_approval_use_case.dart';
import '../../domain/entities/approval_history_record.dart';
import '../../domain/repositories/approval_history_repository.dart';

/// 결재 이력에서 필터링 가능한 행위 타입.
enum ApprovalHistoryActionFilter { all, approve, reject, comment }

/// 결재 이력 화면 탭 종류.
enum ApprovalHistoryTab { flow, audit }

/// 결재 이력 화면의 목록/필터 상태를 관리하는 컨트롤러.
///
/// 기간, 검색어, 행위 타입에 따라 목록을 조회하고 페이지 사이즈를 조절한다.
class ApprovalHistoryController extends ChangeNotifier {
  ApprovalHistoryController({
    required ApprovalHistoryRepository repository,
    ApprovalRepository? approvalRepository,
    RecallApprovalUseCase? recallUseCase,
    ResubmitApprovalUseCase? resubmitUseCase,
  }) : _repository = repository,
       _approvalRepository = approvalRepository,
       _recallUseCase = recallUseCase,
       _resubmitUseCase = resubmitUseCase;

  final ApprovalHistoryRepository _repository;
  final ApprovalRepository? _approvalRepository;
  final RecallApprovalUseCase? _recallUseCase;
  final ResubmitApprovalUseCase? _resubmitUseCase;
  final Map<int, ApprovalFlow> _flowCache = <int, ApprovalFlow>{};

  PaginatedResult<ApprovalHistoryRecord>? _result;
  PaginatedResult<ApprovalHistory>? _auditResult;
  bool _isLoading = false;
  bool _isLoadingAudit = false;
  bool _isPerformingAction = false;
  bool _isLoadingFlow = false;
  ApprovalHistoryTab _activeTab = ApprovalHistoryTab.flow;
  String _query = '';
  ApprovalHistoryActionFilter _actionFilter = ApprovalHistoryActionFilter.all;
  DateTime? _from;
  DateTime? _to;
  String? _errorMessage;
  int _pageSize = 20;
  int _auditPageSize = 20;
  int? _selectedApprovalId;
  ApprovalFlow? _selectedFlow;
  int? _auditActorId;
  String? _auditActionCode;
  DateTime? _auditFrom;
  DateTime? _auditTo;
  final Map<String, ApprovalAction> _auditActions = <String, ApprovalAction>{};
  final Map<String, int> _actionIdsByCode = <String, int>{};
  bool _hasLoadedActionCatalog = false;
  bool _isSelectionForbidden = false;

  PaginatedResult<ApprovalHistoryRecord>? get result => _result;
  bool get isLoading => _isLoading;
  String get query => _query;
  ApprovalHistoryActionFilter get actionFilter => _actionFilter;
  DateTime? get from => _from;
  DateTime? get to => _to;
  String? get errorMessage => _errorMessage;
  int get pageSize => _result?.pageSize ?? _pageSize;
  PaginatedResult<ApprovalHistory>? get auditResult => _auditResult;
  bool get isLoadingAudit => _isLoadingAudit;
  bool get isPerformingAction => _isPerformingAction;
  ApprovalHistoryTab get activeTab => _activeTab;
  int get auditPageSize => _auditResult?.pageSize ?? _auditPageSize;
  int? get selectedApprovalId => _selectedApprovalId;
  bool get hasAuditSelection => _selectedApprovalId != null;
  bool get hasAuditResults => _auditResult?.items.isNotEmpty ?? false;
  bool get isLoadingFlow => _isLoadingFlow;
  ApprovalFlow? get selectedFlow => _selectedFlow;
  int? get auditActorId => _auditActorId;
  String? get auditActionCode => _auditActionCode;
  DateTime? get auditFrom => _auditFrom;
  DateTime? get auditTo => _auditTo;
  List<ApprovalAction> get auditActions {
    if (_auditActions.isEmpty) {
      return const <ApprovalAction>[];
    }
    final items = _auditActions.values.toList(growable: false);
    items.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
    return items;
  }

  bool get isSelectionForbidden => _isSelectionForbidden;

  /// 현재 필터 조건에 맞춰 결재 이력 목록을 불러온다.
  ///
  /// 페이지 번호는 1 이상을 기대하며, 실패 시 [_errorMessage]에 사유를 기록한다.
  Future<void> fetch({int page = 1}) async {
    _isLoading = true;
    _errorMessage = null;
    notifyListeners();
    try {
      final resolvedPage = _resolvePage(page, _result);
      final approvalActionId = await _resolveActionIdForFilter(_actionFilter);

      final response = await _repository.list(
        page: resolvedPage,
        pageSize: _pageSize,
        query: _query.trim().isEmpty ? null : _query.trim(),
        approvalActionId: approvalActionId,
        from: _from,
        to: _to,
      );
      _result = response;
      _pageSize = response.pageSize;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  /// 지정한 결재의 감사 로그를 조회한다.
  Future<void> fetchAuditLogs({required int approvalId, int page = 1}) async {
    final approvalRepository = _approvalRepository;
    if (approvalRepository == null) {
      throw StateError('ApprovalRepository가 주입되지 않았습니다.');
    }
    _isLoadingAudit = true;
    _errorMessage = null;
    _selectedApprovalId = approvalId;
    notifyListeners();
    try {
      final resolvedPage = _resolvePage(page, _auditResult);
      final response = await approvalRepository.listHistory(
        approvalId: approvalId,
        page: resolvedPage,
        pageSize: auditPageSize,
        from: _auditFrom ?? _from,
        to: _auditTo ?? _to,
        actorId: _auditActorId,
        approvalActionId: _resolveAuditActionId(),
      );
      _auditResult = response;
      _auditPageSize = response.pageSize;
      if (response.items.isNotEmpty) {
        final actionMap = <String, ApprovalAction>{};
        for (final log in response.items) {
          final code = log.action.code?.trim();
          if (code == null || code.isEmpty) {
            continue;
          }
          actionMap.putIfAbsent(code, () => log.action);
          _actionIdsByCode[code] = log.action.id;
        }
        if (actionMap.isNotEmpty) {
          _auditActions
            ..clear()
            ..addAll(actionMap);
        }
      }
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      if (failure.statusCode == 403) {
        _isSelectionForbidden = true;
        _auditResult = null;
      }
    } finally {
      _isLoadingAudit = false;
      notifyListeners();
    }
  }

  /// 결재 상세와 타임라인 정보를 조회해 선택 상태를 갱신한다.
  Future<void> loadApprovalFlow(int approvalId, {bool force = false}) async {
    final approvalRepository = _approvalRepository;
    if (approvalRepository == null) {
      throw StateError('ApprovalRepository가 주입되지 않았습니다.');
    }
    if (!force && _flowCache.containsKey(approvalId)) {
      _selectedApprovalId = approvalId;
      _selectedFlow = _flowCache[approvalId];
      notifyListeners();
      return;
    }
    _isLoadingFlow = true;
    _errorMessage = null;
    _selectedApprovalId = approvalId;
    _isSelectionForbidden = false;
    _selectedFlow = null;
    notifyListeners();
    try {
      final detail = await approvalRepository.fetchDetail(
        approvalId,
        includeSteps: true,
        includeHistories: true,
      );
      final flow = ApprovalFlow.fromApproval(detail);
      _flowCache[approvalId] = flow;
      _selectedFlow = flow;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.statusCode == 403
          ? failure.describe()
          : '결재 상세를 새로고침하지 못했습니다. 다시 시도해 주세요.';
      if (failure.statusCode == 403) {
        _isSelectionForbidden = true;
        _selectedFlow = null;
        _auditResult = null;
      }
    } finally {
      _isLoadingFlow = false;
      notifyListeners();
    }
  }

  /// 선택된 결재 흐름을 최신 상태로 갱신한다.
  Future<ApprovalFlow?> refreshFlow(int approvalId) async {
    final approvalRepository = _approvalRepository;
    if (approvalRepository == null) {
      throw StateError('ApprovalRepository가 주입되지 않았습니다.');
    }
    try {
      final detail = await approvalRepository.fetchDetail(
        approvalId,
        includeSteps: true,
        includeHistories: true,
      );
      final flow = ApprovalFlow.fromApproval(detail);
      _flowCache[approvalId] = flow;
      if (_selectedApprovalId == approvalId) {
        _selectedFlow = flow;
        notifyListeners();
      }
      return flow;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.statusCode == 403
          ? failure.describe()
          : '결재 상세를 새로고침하지 못했습니다. 다시 시도해 주세요.';
      notifyListeners();
      return null;
    }
  }

  /// 검색어를 업데이트해 다음 조회 시 적용될 수 있도록 한다.
  void updateQuery(String value) {
    _query = value;
    notifyListeners();
  }

  /// 행위 타입 필터를 갱신한다.
  void updateActionFilter(ApprovalHistoryActionFilter filter) {
    _actionFilter = filter;
    notifyListeners();
  }

  /// 조회 기간을 설정한다. null을 전달하면 해당 조건을 제거한다.
  void updateDateRange(DateTime? from, DateTime? to) {
    _from = from;
    _to = to;
    notifyListeners();
  }

  /// 활성 탭을 변경한다.
  void updateActiveTab(ApprovalHistoryTab tab) {
    if (_activeTab == tab) {
      return;
    }
    _activeTab = tab;
    notifyListeners();
  }

  /// 감사 로그 페이지 사이즈를 변경한다.
  void updateAuditPageSize(int value) {
    if (value <= 0) {
      return;
    }
    _auditPageSize = value;
    notifyListeners();
  }

  /// 감사 로그 행위자를 필터링한다.
  void updateAuditActor(int? actorId) {
    final normalized = actorId != null && actorId > 0 ? actorId : null;
    if (_auditActorId == normalized) {
      return;
    }
    _auditActorId = normalized;
    notifyListeners();
  }

  /// 감사 로그 행위 타입을 필터링한다.
  void updateAuditAction(String? actionCode) {
    final normalized = actionCode?.trim();
    if (normalized != null && normalized.isEmpty) {
      _auditActionCode = null;
    } else {
      if (_auditActionCode == normalized) {
        return;
      }
      _auditActionCode = normalized;
    }
    notifyListeners();
  }

  /// 감사 로그 기간 필터를 설정한다.
  void updateAuditDateRange(DateTime? from, DateTime? to) {
    DateTime? normalizedFrom = from;
    DateTime? normalizedTo = to;
    if (normalizedFrom != null && normalizedTo != null) {
      if (normalizedFrom.isAfter(normalizedTo)) {
        final temp = normalizedFrom;
        normalizedFrom = normalizedTo;
        normalizedTo = temp;
      }
    }
    if (_auditFrom == normalizedFrom && _auditTo == normalizedTo) {
      return;
    }
    _auditFrom = normalizedFrom;
    _auditTo = normalizedTo;
    notifyListeners();
  }

  /// 감사 로그 필터를 초기화한다.
  void clearAuditFilters() {
    if (_auditActorId == null &&
        (_auditActionCode == null || _auditActionCode!.isEmpty) &&
        _auditFrom == null &&
        _auditTo == null) {
      return;
    }
    _auditActorId = null;
    _auditActionCode = null;
    _auditFrom = null;
    _auditTo = null;
    notifyListeners();
  }

  /// 검색어/행위/기간 필터를 초기화한다.
  void clearFilters() {
    _query = '';
    _actionFilter = ApprovalHistoryActionFilter.all;
    _from = null;
    _to = null;
    notifyListeners();
  }

  /// 감사 로그 선택 상태를 초기화한다.
  void clearAuditSelection() {
    clearSelection();
  }

  /// 선택된 결재의 감사 로그를 새로고침한다.
  Future<void> refreshAudit() async {
    final approvalId = _selectedApprovalId;
    if (approvalId == null) {
      return;
    }
    await fetchAuditLogs(approvalId: approvalId, page: _auditResult?.page ?? 1);
  }

  /// 축적된 오류 메시지를 초기화한다.
  void clearError() {
    _errorMessage = null;
    notifyListeners();
  }

  /// 페이지 사이즈를 변경한다. 0 이하 값은 무시한다.
  void updatePageSize(int value) {
    if (value <= 0) {
      return;
    }
    _pageSize = value;
    notifyListeners();
  }

  /// 결재를 회수한다.
  Future<ApprovalFlow?> recallApproval(ApprovalRecallInput input) async {
    final useCase = _recallUseCase;
    if (useCase == null) {
      throw StateError('RecallApprovalUseCase가 주입되지 않았습니다.');
    }
    _isPerformingAction = true;
    _errorMessage = null;
    notifyListeners();
    try {
      final flow = await useCase.call(input);
      final targetId = flow.approval.id ?? input.approvalId;
      await _refreshAfterAction(targetId, flow: flow);
      return flow;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      return null;
    } finally {
      _isPerformingAction = false;
      notifyListeners();
    }
  }

  /// 결재를 재상신한다.
  Future<ApprovalFlow?> resubmitApproval(
    ApprovalResubmissionInput input,
  ) async {
    final useCase = _resubmitUseCase;
    if (useCase == null) {
      throw StateError('ResubmitApprovalUseCase가 주입되지 않았습니다.');
    }
    _isPerformingAction = true;
    _errorMessage = null;
    notifyListeners();
    try {
      final flow = await useCase.call(input);
      final targetId = flow.approval.id ?? input.approvalId;
      await _refreshAfterAction(targetId, flow: flow);
      return flow;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      return null;
    } finally {
      _isPerformingAction = false;
      notifyListeners();
    }
  }

  bool get hasActiveFilters =>
      _query.trim().isNotEmpty ||
      _actionFilter != ApprovalHistoryActionFilter.all ||
      _from != null ||
      _to != null;

  bool get hasActiveAuditFilters =>
      (_auditActorId ?? 0) > 0 ||
      (_auditActionCode != null && _auditActionCode!.trim().isNotEmpty) ||
      _auditFrom != null ||
      _auditTo != null;

  Future<int?> _resolveActionIdForFilter(
    ApprovalHistoryActionFilter filter,
  ) async {
    final code = _codeForFilter(filter);
    if (code == null) {
      return null;
    }
    final cached = _actionIdsByCode[code];
    if (cached != null) {
      return cached;
    }
    final auditAction = _auditActions[code];
    if (auditAction != null) {
      final id = auditAction.id;
      _actionIdsByCode[code] = id;
      return id;
    }
    await _ensureActionCatalogLoaded();
    return _actionIdsByCode[code];
  }

  String? _codeForFilter(ApprovalHistoryActionFilter filter) {
    return switch (filter) {
      ApprovalHistoryActionFilter.all => null,
      ApprovalHistoryActionFilter.approve => 'approve',
      ApprovalHistoryActionFilter.reject => 'reject',
      ApprovalHistoryActionFilter.comment => 'comment',
    };
  }

  Future<void> _ensureActionCatalogLoaded() async {
    if (_hasLoadedActionCatalog) {
      return;
    }
    final approvalRepository = _approvalRepository;
    if (approvalRepository == null) {
      _hasLoadedActionCatalog = true;
      return;
    }
    try {
      final actions = await approvalRepository.listActions();
      for (final action in actions) {
        final code = action.code?.trim();
        if (code == null || code.isEmpty) {
          continue;
        }
        _actionIdsByCode.putIfAbsent(code, () => action.id);
      }
      _hasLoadedActionCatalog = true;
    } catch (_) {
      // 재시도를 위해 로드 여부 플래그를 유지한다.
    }
  }

  int? _resolveAuditActionId() {
    final code = _auditActionCode?.trim();
    if (code == null || code.isEmpty) {
      return null;
    }
    final action = _auditActions[code];
    return action?.id;
  }

  /// 현재 선택 상태와 캐시를 초기화한다.
  void clearSelection() {
    if (_selectedApprovalId == null &&
        _auditResult == null &&
        _selectedFlow == null) {
      return;
    }
    _selectedApprovalId = null;
    _selectedFlow = null;
    _auditResult = null;
    _isSelectionForbidden = false;
    notifyListeners();
  }

  int _resolvePage(int requested, PaginatedResult<dynamic>? current) {
    if (requested < 1) {
      return 1;
    }
    if (current != null && current.pageSize > 0) {
      final calculated = (current.total / current.pageSize).ceil();
      final maxPage = calculated < 1 ? 1 : calculated;
      return requested > maxPage ? maxPage : requested;
    }
    return requested;
  }

  Future<void> _refreshAfterAction(int approvalId, {ApprovalFlow? flow}) async {
    await fetch(page: _result?.page ?? 1);
    if (flow != null) {
      _flowCache[approvalId] = flow;
      if (_selectedApprovalId == approvalId) {
        _selectedFlow = flow;
        notifyListeners();
      }
    }
    if (_selectedApprovalId == approvalId) {
      if (flow == null && _approvalRepository != null) {
        await loadApprovalFlow(approvalId, force: true);
      }
      if (_approvalRepository != null) {
        await fetchAuditLogs(
          approvalId: approvalId,
          page: _auditResult?.page ?? 1,
        );
      }
    }
  }
}
