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 '../../../domain/entities/approval_flow.dart';
import '../../../domain/entities/approval_template.dart';
import '../../../domain/repositories/approval_template_repository.dart';
import '../../../domain/usecases/apply_approval_template_use_case.dart';
import '../../../domain/usecases/save_approval_template_use_case.dart';

/// 결재 템플릿 목록에서 사용할 상태 필터.
enum ApprovalTemplateStatusFilter { all, activeOnly, inactiveOnly }

/// 결재 템플릿 화면 상태 컨트롤러
///
/// - 목록/검색/필터 상태와 생성·수정·삭제 요청을 관리한다.
class ApprovalTemplateController extends ChangeNotifier {
  ApprovalTemplateController({
    required ApprovalTemplateRepository repository,
    SaveApprovalTemplateUseCase? saveTemplateUseCase,
    ApplyApprovalTemplateUseCase? applyTemplateUseCase,
  }) : _repository = repository,
       _saveTemplateUseCase = saveTemplateUseCase,
       _applyTemplateUseCase = applyTemplateUseCase;

  final ApprovalTemplateRepository _repository;
  final SaveApprovalTemplateUseCase? _saveTemplateUseCase;
  final ApplyApprovalTemplateUseCase? _applyTemplateUseCase;
  final Map<int, DateTime?> _templateVersions = <int, DateTime?>{};
  final Map<int, List<ApprovalTemplateStep>> _templateStepSummaries =
      <int, List<ApprovalTemplateStep>>{};

  PaginatedResult<ApprovalTemplate>? _result;
  bool _isLoading = false;
  bool _isSubmitting = false;
  bool _isApplyingTemplate = false;
  String _query = '';
  ApprovalTemplateStatusFilter _statusFilter = ApprovalTemplateStatusFilter.all;
  String? _errorMessage;
  int _pageSize = 20;

  PaginatedResult<ApprovalTemplate>? get result => _result;
  bool get isLoading => _isLoading;
  bool get isSubmitting => _isSubmitting;
  String get query => _query;
  ApprovalTemplateStatusFilter get statusFilter => _statusFilter;
  String? get errorMessage => _errorMessage;
  int get pageSize => _result?.pageSize ?? _pageSize;
  bool get isApplyingTemplate => _isApplyingTemplate;
  UnmodifiableMapView<int, DateTime?> get templateVersions =>
      UnmodifiableMapView(_templateVersions);
  UnmodifiableMapView<int, List<ApprovalTemplateStep>>
  get templateStepSummaries => UnmodifiableMapView(_templateStepSummaries);

  /// 캐시된 템플릿 버전 정보를 반환한다.
  DateTime? versionOf(int templateId) => _templateVersions[templateId];

  /// 캐시된 단계 요약을 반환한다.
  List<ApprovalTemplateStep>? stepSummaryOf(int templateId) =>
      _templateStepSummaries[templateId];

  /// 단계 요약이 없으면 상세를 조회해 캐시한다.
  Future<List<ApprovalTemplateStep>?> ensureStepSummary(int templateId) async {
    final cached = _templateStepSummaries[templateId];
    if (cached != null && cached.isNotEmpty) {
      return cached;
    }
    final detail = await fetchDetail(templateId);
    return detail?.steps;
  }

  /// 서버 업데이트 일시와 비교해 로컬 버전이 뒤처졌는지 확인한다.
  bool isTemplateStale(int templateId, DateTime? remoteUpdatedAt) {
    final local = _templateVersions[templateId];
    if (local == null || remoteUpdatedAt == null) {
      return false;
    }
    return local.isBefore(remoteUpdatedAt);
  }

  /// 템플릿 목록을 조회해 캐시에 저장한다.
  ///
  /// 검색어/상태 조건을 함께 적용하며, 실패 시 [_errorMessage]에 예외 메시지를 기록한다.
  Future<void> fetch({int page = 1}) async {
    _isLoading = true;
    _errorMessage = null;
    notifyListeners();
    try {
      final previous = _result;
      final int resolvedPage;
      if (page < 1) {
        resolvedPage = 1;
      } else if (previous != null && previous.pageSize > 0) {
        final calculated = (previous.total / previous.pageSize).ceil();
        final maxPage = calculated < 1 ? 1 : calculated;
        resolvedPage = page > maxPage ? maxPage : page;
      } else {
        resolvedPage = page;
      }
      final sanitizedQuery = _query.trim();
      final isActive = switch (_statusFilter) {
        ApprovalTemplateStatusFilter.all => null,
        ApprovalTemplateStatusFilter.activeOnly => true,
        ApprovalTemplateStatusFilter.inactiveOnly => false,
      };
      final response = await _repository.list(
        page: resolvedPage,
        pageSize: _pageSize,
        query: sanitizedQuery.isEmpty ? null : sanitizedQuery,
        isActive: isActive,
      );
      _result = response;
      _pageSize = response.pageSize;
      _recordTemplateVersions(response.items);
      for (final template in response.items) {
        _cacheTemplateSteps(template);
      }
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  /// 검색어 조건을 갱신해 다음 조회에 반영한다.
  void updateQuery(String value) {
    _query = value;
    notifyListeners();
  }

  /// 템플릿 상태 필터를 변경한다.
  void updateStatusFilter(ApprovalTemplateStatusFilter filter) {
    _statusFilter = filter;
    notifyListeners();
  }

  /// 특정 템플릿의 상세 정보를 조회한다.
  ///
  /// 단계 정보까지 포함해 반환하며 실패 시 null을 반환하고 오류 메시지를 보관한다.
  Future<ApprovalTemplate?> fetchDetail(int id) async {
    _errorMessage = null;
    notifyListeners();
    try {
      final detail = await _repository.fetchDetail(id, includeSteps: true);
      _recordTemplateVersion(detail);
      _cacheTemplateSteps(detail);
      notifyListeners();
      return detail;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      return null;
    }
  }

  /// 새 템플릿을 생성하고 목록을 1페이지로 새로 고친다.
  Future<ApprovalTemplate?> create(
    ApprovalTemplateInput input,
    List<ApprovalTemplateStepInput> steps,
  ) {
    return _saveTemplate(
      templateId: null,
      input: input,
      steps: steps,
      refreshPage: 1,
    );
  }

  /// 기존 템플릿을 수정하고 현재 페이지를 유지한 채 목록을 다시 가져온다.
  Future<ApprovalTemplate?> update(
    int id,
    ApprovalTemplateInput input,
    List<ApprovalTemplateStepInput>? steps,
  ) {
    return _saveTemplate(
      templateId: id,
      input: input,
      steps: steps,
      refreshPage: _result?.page ?? 1,
    );
  }

  /// 템플릿을 저장(create/update)하는 공통 진입점.
  Future<ApprovalTemplate?> save({
    int? templateId,
    required ApprovalTemplateInput input,
    List<ApprovalTemplateStepInput>? steps,
  }) {
    final refreshPage = templateId == null ? 1 : _result?.page ?? 1;
    return _saveTemplate(
      templateId: templateId,
      input: input,
      steps: steps,
      refreshPage: refreshPage,
    );
  }

  /// 템플릿을 삭제(비활성화)한 뒤 목록을 재조회한다.
  Future<bool> delete(int id) async {
    _setSubmitting(true);
    try {
      await _repository.delete(id);
      await fetch(page: _result?.page ?? 1);
      return true;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      return false;
    } finally {
      _setSubmitting(false);
    }
  }

  /// 삭제된 템플릿을 복구한 뒤 목록을 최신 상태로 만든다.
  Future<ApprovalTemplate?> restore(int id) async {
    _setSubmitting(true);
    try {
      final restored = await _repository.restore(id);
      _recordTemplateVersion(restored);
      await fetch(page: _result?.page ?? 1);
      return restored;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      return null;
    } finally {
      _setSubmitting(false);
    }
  }

  /// 템플릿을 지정한 결재에 적용한다.
  Future<ApprovalFlow?> applyToApproval({
    required int approvalId,
    required int templateId,
  }) async {
    final useCase = _applyTemplateUseCase;
    if (useCase == null) {
      throw StateError('ApplyApprovalTemplateUseCase가 주입되지 않았습니다.');
    }
    _errorMessage = null;
    _isApplyingTemplate = true;
    notifyListeners();
    try {
      final flow = await useCase.call(
        approvalId: approvalId,
        templateId: templateId,
      );
      try {
        final template = await _repository.fetchDetail(
          templateId,
          includeSteps: false,
        );
        _recordTemplateVersion(template);
      } catch (_) {
        // 최신 템플릿 버전 조회 실패는 무시한다.
      }
      return flow;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      return null;
    } finally {
      _isApplyingTemplate = false;
      notifyListeners();
    }
  }

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

  bool get hasActiveFilters =>
      _query.trim().isNotEmpty ||
      _statusFilter != ApprovalTemplateStatusFilter.all;

  /// 검색어와 상태 필터를 기본값으로 되돌린다.
  void resetFilters() {
    _query = '';
    _statusFilter = ApprovalTemplateStatusFilter.all;
    notifyListeners();
  }

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

  /// 네트워크 작업 진행 여부를 갱신한다.
  void _setSubmitting(bool value) {
    _isSubmitting = value;
    notifyListeners();
  }

  Future<ApprovalTemplate?> _saveTemplate({
    int? templateId,
    required ApprovalTemplateInput input,
    List<ApprovalTemplateStepInput>? steps,
    required int refreshPage,
  }) async {
    _errorMessage = null;
    _setSubmitting(true);
    try {
      final template = await _performSave(templateId, input, steps);
      _recordTemplateVersion(template);
      await fetch(page: refreshPage);
      return template;
    } catch (error) {
      final failure = Failure.from(error);
      _errorMessage = failure.describe();
      notifyListeners();
      return null;
    } finally {
      _setSubmitting(false);
    }
  }

  Future<ApprovalTemplate> _performSave(
    int? templateId,
    ApprovalTemplateInput input,
    List<ApprovalTemplateStepInput>? steps,
  ) {
    final useCase = _saveTemplateUseCase;
    if (useCase != null) {
      return useCase.call(templateId: templateId, input: input, steps: steps);
    }
    if (templateId == null) {
      return _repository.create(input, steps: steps ?? const []);
    }
    return _repository.update(templateId, input, steps: steps);
  }

  void _recordTemplateVersions(Iterable<ApprovalTemplate> templates) {
    for (final template in templates) {
      _recordTemplateVersion(template);
    }
  }

  void _recordTemplateVersion(ApprovalTemplate template) {
    _templateVersions[template.id] = template.updatedAt;
  }

  void _cacheTemplateSteps(ApprovalTemplate template) {
    if (template.steps.isEmpty) {
      return;
    }
    _templateStepSummaries[template.id] = List<ApprovalTemplateStep>.from(
      template.steps,
    );
  }
}
