/// 결재(Approval) 엔티티
///
/// - 결재 기본 정보와 현재 단계, 라인(단계/이력) 데이터를 포함한다.
/// - presentation/data 레이어 구현에 의존하지 않는다.
class Approval {
  Approval({
    this.id,
    required this.approvalNo,
    this.transactionId,
    required this.transactionNo,
    this.transactionUpdatedAt,
    required this.status,
    this.currentStep,
    required this.requester,
    required this.requestedAt,
    this.decidedAt,
    this.note,
    this.isActive = true,
    this.isDeleted = false,
    this.steps = const [],
    this.histories = const [],
    this.createdAt,
    this.updatedAt,
  });

  final int? id;
  final String approvalNo;
  final int? transactionId;
  final String transactionNo;
  final DateTime? transactionUpdatedAt;
  final ApprovalStatus status;
  final ApprovalStep? currentStep;
  final ApprovalRequester requester;
  final DateTime requestedAt;
  final DateTime? decidedAt;
  final String? note;
  final bool isActive;
  final bool isDeleted;
  final List<ApprovalStep> steps;
  final List<ApprovalHistory> histories;
  final DateTime? createdAt;
  final DateTime? updatedAt;

  Approval copyWith({
    int? id,
    String? approvalNo,
    int? transactionId,
    String? transactionNo,
    DateTime? transactionUpdatedAt,
    ApprovalStatus? status,
    ApprovalStep? currentStep,
    ApprovalRequester? requester,
    DateTime? requestedAt,
    DateTime? decidedAt,
    String? note,
    bool? isActive,
    bool? isDeleted,
    List<ApprovalStep>? steps,
    List<ApprovalHistory>? histories,
    DateTime? createdAt,
    DateTime? updatedAt,
  }) {
    return Approval(
      id: id ?? this.id,
      approvalNo: approvalNo ?? this.approvalNo,
      transactionId: transactionId ?? this.transactionId,
      transactionNo: transactionNo ?? this.transactionNo,
      transactionUpdatedAt: transactionUpdatedAt ?? this.transactionUpdatedAt,
      status: status ?? this.status,
      currentStep: currentStep ?? this.currentStep,
      requester: requester ?? this.requester,
      requestedAt: requestedAt ?? this.requestedAt,
      decidedAt: decidedAt ?? this.decidedAt,
      note: note ?? this.note,
      isActive: isActive ?? this.isActive,
      isDeleted: isDeleted ?? this.isDeleted,
      steps: steps ?? this.steps,
      histories: histories ?? this.histories,
      createdAt: createdAt ?? this.createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
    );
  }
}

class ApprovalStatus {
  ApprovalStatus({
    required this.id,
    required this.name,
    this.color,
    this.isBlockingNext = true,
    this.isTerminal = false,
  });

  final int id;
  final String name;
  final String? color;
  final bool isBlockingNext;
  final bool isTerminal;

  ApprovalStatus copyWith({
    int? id,
    String? name,
    String? color,
    bool? isBlockingNext,
    bool? isTerminal,
  }) {
    return ApprovalStatus(
      id: id ?? this.id,
      name: name ?? this.name,
      color: color ?? this.color,
      isBlockingNext: isBlockingNext ?? this.isBlockingNext,
      isTerminal: isTerminal ?? this.isTerminal,
    );
  }
}

class ApprovalRequester {
  ApprovalRequester({
    required this.id,
    required this.employeeNo,
    required this.name,
  });

  final int id;
  final String employeeNo;
  final String name;
}

class ApprovalStep {
  ApprovalStep({
    this.id,
    this.requestId,
    required this.stepOrder,
    this.templateStepId,
    this.approverRole,
    required this.approver,
    required this.status,
    required this.assignedAt,
    this.decidedAt,
    this.note,
    this.isDeleted = false,
    this.actionAt,
    this.isOptional = false,
    this.escalationMinutes,
    this.metadata,
  });

  final int? id;
  final int? requestId;
  final int stepOrder;
  final int? templateStepId;
  final String? approverRole;
  final ApprovalApprover approver;
  final ApprovalStatus status;
  final DateTime assignedAt;
  final DateTime? decidedAt;
  final String? note;
  final bool isDeleted;
  final DateTime? actionAt;
  final bool isOptional;
  final int? escalationMinutes;
  final Map<String, dynamic>? metadata;

  ApprovalStep copyWith({
    int? id,
    int? requestId,
    int? stepOrder,
    int? templateStepId,
    String? approverRole,
    ApprovalApprover? approver,
    ApprovalStatus? status,
    DateTime? assignedAt,
    DateTime? decidedAt,
    String? note,
    bool? isDeleted,
    DateTime? actionAt,
    bool? isOptional,
    int? escalationMinutes,
    Map<String, dynamic>? metadata,
  }) {
    return ApprovalStep(
      id: id ?? this.id,
      requestId: requestId ?? this.requestId,
      stepOrder: stepOrder ?? this.stepOrder,
      templateStepId: templateStepId ?? this.templateStepId,
      approverRole: approverRole ?? this.approverRole,
      approver: approver ?? this.approver,
      status: status ?? this.status,
      assignedAt: assignedAt ?? this.assignedAt,
      decidedAt: decidedAt ?? this.decidedAt,
      note: note ?? this.note,
      isDeleted: isDeleted ?? this.isDeleted,
      actionAt: actionAt ?? this.actionAt,
      isOptional: isOptional ?? this.isOptional,
      escalationMinutes: escalationMinutes ?? this.escalationMinutes,
      metadata: metadata ?? this.metadata,
    );
  }
}

class ApprovalApprover {
  ApprovalApprover({
    required this.id,
    required this.employeeNo,
    required this.name,
  });

  final int id;
  final String employeeNo;
  final String name;
}

class ApprovalHistory {
  ApprovalHistory({
    this.id,
    required this.action,
    this.fromStatus,
    required this.toStatus,
    required this.approver,
    required this.actionAt,
    this.note,
    this.actionCode,
    this.payload,
  });

  final int? id;
  final ApprovalAction action;
  final ApprovalStatus? fromStatus;
  final ApprovalStatus toStatus;
  final ApprovalApprover approver;
  final DateTime actionAt;
  final String? note;
  final String? actionCode;
  final Map<String, dynamic>? payload;
}

class ApprovalAction {
  ApprovalAction({required this.id, required this.name, this.code});

  final int id;
  final String name;
  final String? code;
}

/// 결재 단계에서 수행 가능한 행위 타입
///
/// - API `approval_actions` 테이블의 대표 코드와 매핑된다.
/// - UI에서는 이 타입을 기반으로 표시 라벨과 권한을 제어한다.
enum ApprovalStepActionType { approve, reject, comment }

extension ApprovalStepActionTypeX on ApprovalStepActionType {
  /// API 호출 시 사용되는 행위 코드
  String get code {
    switch (this) {
      case ApprovalStepActionType.approve:
        return 'approve';
      case ApprovalStepActionType.reject:
        return 'reject';
      case ApprovalStepActionType.comment:
        return 'comment';
    }
  }
}

/// 결재 생성 입력 모델
/// 결재 신규 생성 입력 모델
///
/// - 트랜잭션, 결재번호, 상태, 상신자 정보를 백엔드 계약에 맞춰 전달한다.
class ApprovalCreateInput {
  ApprovalCreateInput({
    required this.transactionId,
    required this.approvalStatusId,
    required this.requestedById,
    this.note,
  });

  final int transactionId;
  final int approvalStatusId;
  final int requestedById;
  final String? note;

  Map<String, dynamic> toPayload() {
    final trimmedNote = note?.trim();
    return {
      'transaction_id': transactionId,
      'approval_status_id': approvalStatusId,
      'requested_by_id': requestedById,
      if (trimmedNote != null && trimmedNote.isNotEmpty) 'note': trimmedNote,
    };
  }
}

/// 결재 기본 정보 수정 입력 모델
///
/// - 상태/비고 변경 시 결재 식별자를 포함해 패치를 수행한다.
class ApprovalUpdateInput {
  ApprovalUpdateInput({required this.id, this.approvalStatusId, this.note});

  final int id;
  final int? approvalStatusId;
  final String? note;

  Map<String, dynamic> toPayload() {
    final trimmedNote = note?.trim();
    return {
      'id': id,
      if (approvalStatusId != null) 'approval_status_id': approvalStatusId,
      if (trimmedNote != null && trimmedNote.isNotEmpty) 'note': trimmedNote,
    };
  }
}

/// 결재 단계 행위 입력 모델
///
/// - `POST /approval-steps/{id}/actions` 요청 바디를 구성한다.
/// - `note`는 비고가 있을 때만 포함한다.
class ApprovalStepActionInput {
  ApprovalStepActionInput({
    required this.stepId,
    required this.actionId,
    this.note,
  });

  final int stepId;
  final int actionId;
  final String? note;

  Map<String, dynamic> toPayload() {
    return {
      'id': stepId,
      'approval_action_id': actionId,
      if (note != null && note!.trim().isNotEmpty) 'note': note,
    };
  }
}

/// 결재 단계를 일괄 등록/재배치하기 위한 입력 모델
class ApprovalStepAssignmentInput {
  ApprovalStepAssignmentInput({required this.approvalId, required this.steps});

  final int approvalId;
  final List<ApprovalStepAssignmentItem> steps;

  Map<String, dynamic> toPayload() {
    return {'id': approvalId, 'steps': steps.map((e) => e.toJson()).toList()};
  }
}

class ApprovalStepAssignmentItem {
  ApprovalStepAssignmentItem({
    required this.stepOrder,
    required this.approverId,
    this.note,
  });

  final int stepOrder;
  final int approverId;
  final String? note;

  Map<String, dynamic> toJson() {
    return {
      'step_order': stepOrder,
      'approver_id': approverId,
      if (note != null && note!.trim().isNotEmpty) 'note': note,
    };
  }
}

/// 결재 상신 입력 모델.
class ApprovalSubmissionInput {
  ApprovalSubmissionInput({
    this.transactionId,
    this.templateId,
    required this.statusId,
    required this.requesterId,
    this.finalApproverId,
    this.requestedAt,
    this.decidedAt,
    this.cancelledAt,
    this.lastActionAt,
    this.title,
    this.summary,
    this.note,
    this.metadata,
    this.steps = const [],
  });

  final int? transactionId;
  final int? templateId;
  final int statusId;
  final int requesterId;
  final int? finalApproverId;
  final DateTime? requestedAt;
  final DateTime? decidedAt;
  final DateTime? cancelledAt;
  final DateTime? lastActionAt;
  final String? title;
  final String? summary;
  final String? note;
  final Map<String, dynamic>? metadata;
  final List<ApprovalStepAssignmentItem> steps;
}

/// 결재 승인/반려 입력 모델.
class ApprovalDecisionInput {
  ApprovalDecisionInput({
    required this.approvalId,
    required this.actorId,
    this.note,
    this.expectedUpdatedAt,
  });

  final int approvalId;
  final int actorId;
  final String? note;
  final DateTime? expectedUpdatedAt;
}

/// 결재 회수 입력 모델.
class ApprovalRecallInput extends ApprovalDecisionInput {
  ApprovalRecallInput({
    required super.approvalId,
    required super.actorId,
    super.note,
    super.expectedUpdatedAt,
    this.transactionExpectedUpdatedAt,
  });

  final DateTime? transactionExpectedUpdatedAt;
}

/// 결재 재상신 입력 모델.
class ApprovalResubmissionInput {
  ApprovalResubmissionInput({
    required this.approvalId,
    required this.actorId,
    required this.submission,
    this.note,
    this.expectedUpdatedAt,
    this.transactionExpectedUpdatedAt,
  });

  final int approvalId;
  final int actorId;
  final ApprovalSubmissionInput submission;
  final String? note;
  final DateTime? expectedUpdatedAt;
  final DateTime? transactionExpectedUpdatedAt;
}
