import '../entities/approval.dart';

/// 결재 흐름(Approval Flow)을 표현하는 도메인 엔티티.
///
/// - 상신자, 최종 승인자, 단계 목록, 이력, 상태 요약을 한 번에 제공한다.
/// - presentation 레이어에서는 이 엔티티만 의존해 UI를 구성한다.
class ApprovalFlow {
  ApprovalFlow({
    required Approval approval,
    ApprovalApprover? finalApprover,
    ApprovalFlowStatusSummary? statusSummary,
  }) : _approval = approval,
       finalApprover = finalApprover ?? _inferFinalApprover(approval.steps),
       statusSummary =
           statusSummary ??
           ApprovalFlowStatusSummary.from(
             status: approval.status,
             steps: approval.steps,
             currentStep: approval.currentStep,
           ),
       _steps = List<ApprovalStep>.unmodifiable(approval.steps),
       _histories = List<ApprovalHistory>.unmodifiable(approval.histories);

  /// 결재 원본 데이터
  final Approval _approval;

  /// 결재 단계 목록
  final List<ApprovalStep> _steps;

  /// 결재 이력 목록
  final List<ApprovalHistory> _histories;

  /// 최종 승인자 정보 (단계 목록 기반 추론 결과)
  final ApprovalApprover? finalApprover;

  /// 결재 상태 요약 정보
  final ApprovalFlowStatusSummary statusSummary;

  /// 원본 결재 엔티티에 접근한다.
  Approval get approval => _approval;

  /// 결재 식별자(ID)
  int? get id => _approval.id;

  /// 결재 번호(APP-YYYYMMDDNNNN 형식)
  String get approvalNo => _approval.approvalNo;

  /// 연동된 전표 번호
  String get transactionNo => _approval.transactionNo;

  /// 연동된 전표 ID
  int? get transactionId => _approval.transactionId;

  /// 연동된 전표 최신 수정 시각
  DateTime? get transactionUpdatedAt => _approval.transactionUpdatedAt;

  /// 현재 결재 상태
  ApprovalStatus get status => _approval.status;

  /// 현재 진행 중인 단계 정보
  ApprovalStep? get currentStep => _approval.currentStep;

  /// 상신자 정보
  ApprovalRequester get requester => _approval.requester;

  /// 상신 일시
  DateTime get requestedAt => _approval.requestedAt;

  /// 최종 결정 일시
  DateTime? get decidedAt => _approval.decidedAt;

  /// 결재 메모
  String? get note => _approval.note;

  /// 생성 일시
  DateTime? get createdAt => _approval.createdAt;

  /// 변경 일시
  DateTime? get updatedAt => _approval.updatedAt;

  /// 단계 목록을 반환한다.
  List<ApprovalStep> get steps => _steps;

  /// 이력 목록을 반환한다.
  List<ApprovalHistory> get histories => _histories;

  /// [Approval] 엔티티에서 [ApprovalFlow]를 생성하는 팩토리.
  factory ApprovalFlow.fromApproval(Approval approval) =>
      ApprovalFlow(approval: approval);

  static ApprovalApprover? _inferFinalApprover(List<ApprovalStep> steps) {
    if (steps.isEmpty) {
      return null;
    }
    final sorted = List<ApprovalStep>.from(steps)
      ..sort((a, b) => a.stepOrder.compareTo(b.stepOrder));
    return sorted.last.approver;
  }
}

/// 결재 상태 요약 정보.
///
/// - 전체 단계 수, 완료 단계 수, 대기 단계 수, 현재 단계 순번을 제공한다.
class ApprovalFlowStatusSummary {
  ApprovalFlowStatusSummary({
    required this.status,
    required this.totalSteps,
    required this.completedSteps,
    required this.pendingSteps,
    this.currentStepOrder,
  });

  /// 전체 결재 상태
  final ApprovalStatus status;

  /// 총 단계 수
  final int totalSteps;

  /// 완료된 단계 수
  final int completedSteps;

  /// 대기 중인 단계 수
  final int pendingSteps;

  /// 현재 진행 중인 단계 순번 (없으면 null)
  final int? currentStepOrder;

  /// 완료율(%)을 정수로 반환한다.
  int get completionRate {
    if (totalSteps <= 0) {
      return 0;
    }
    final ratio = (completedSteps / totalSteps) * 100;
    return ratio.isFinite ? ratio.round() : 0;
  }

  /// 결재 상태와 단계 목록을 기반으로 요약 정보를 생성한다.
  factory ApprovalFlowStatusSummary.from({
    required ApprovalStatus status,
    required List<ApprovalStep> steps,
    ApprovalStep? currentStep,
  }) {
    final total = steps.length;
    final completed = steps.where((step) => step.decidedAt != null).length;
    final pending = total - completed;
    final currentOrder = currentStep?.stepOrder ?? _findCurrentStepOrder(steps);
    return ApprovalFlowStatusSummary(
      status: status,
      totalSteps: total,
      completedSteps: completed,
      pendingSteps: pending < 0 ? 0 : pending,
      currentStepOrder: currentOrder,
    );
  }

  static int? _findCurrentStepOrder(List<ApprovalStep> steps) {
    for (final step in steps) {
      if (step.decidedAt == null) {
        return step.stepOrder;
      }
    }
    if (steps.isEmpty) {
      return null;
    }
    return steps.last.stepOrder;
  }
}
