import 'package:superport_v2/core/common/utils/json_utils.dart';

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

/// 결재 감사 로그(Audit) DTO.
class ApprovalAuditDto {
  ApprovalAuditDto({
    this.id,
    required this.action,
    this.fromStatus,
    required this.toStatus,
    required this.actor,
    required this.actionAt,
    this.note,
    this.actionCode,
    this.payload,
  });

  final int? id;
  final ApprovalActionDto action;
  final ApprovalStatusDto? fromStatus;
  final ApprovalStatusDto toStatus;
  final ApprovalApproverDto actor;
  final DateTime actionAt;
  final String? note;
  final String? actionCode;
  final Map<String, dynamic>? payload;

  factory ApprovalAuditDto.fromJson(Map<String, dynamic> json) {
    final actionMap = {
      ...?_asMap(json['action']),
      ...?_asMap(json['approval_action']),
    };
    final fallbackActionId = json['approval_action_id'] ?? json['action_id'];
    if (fallbackActionId != null) {
      actionMap.putIfAbsent('id', () => fallbackActionId);
    }
    final fallbackActionName = _firstNonEmpty(<String?>[
      _readString(json, 'action_name'),
      _readString(json, 'approval_action_name'),
    ]);
    if (fallbackActionName != null) {
      actionMap.putIfAbsent('name', () => fallbackActionName);
    }
    final rootActionCode = _firstNonEmpty(<String?>[
      _readString(json, 'action_code'),
      _readString(json, 'approval_action_code'),
    ]);
    if (rootActionCode != null) {
      actionMap.putIfAbsent('code', () => rootActionCode);
      actionMap.putIfAbsent('action_code', () => rootActionCode);
    }
    final actionDto = ApprovalActionDto.fromJson(actionMap);
    final fromStatusMap = _asMap(json['from_status']);
    final toStatusMap = _asMap(json['to_status']) ?? const <String, dynamic>{};
    final actorMap =
        _asMap(json['actor']) ??
        _asMap(json['approver']) ??
        const <String, dynamic>{};
    final resolvedActionCode = rootActionCode ?? actionDto.code;

    return ApprovalAuditDto(
      id: JsonUtils.readInt(json, 'id'),
      action: actionDto,
      fromStatus: fromStatusMap == null
          ? null
          : ApprovalStatusDto.fromJson(fromStatusMap),
      toStatus: ApprovalStatusDto.fromJson(toStatusMap),
      actor: ApprovalApproverDto.fromJson(actorMap),
      actionAt: _parseDate(json['action_at']) ?? DateTime.now(),
      note: _readString(json, 'note'),
      actionCode: resolvedActionCode,
      payload: _asMap(
        json['payload'],
      )?.map((key, value) => MapEntry(key, value)),
    );
  }

  ApprovalHistory toEntity() => ApprovalHistory(
    id: id,
    action: action.toEntity(),
    fromStatus: fromStatus?.toEntity(),
    toStatus: toStatus.toEntity(),
    approver: actor.toEntity(),
    actionAt: actionAt,
    note: note,
    actionCode: actionCode,
    payload: payload,
  );
}

/// 결재 감사 로그 액션 DTO.
class ApprovalActionDto {
  ApprovalActionDto({required this.id, required this.name, this.code});

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

  factory ApprovalActionDto.fromJson(Map<String, dynamic> json) {
    if (json['action'] is Map<String, dynamic>) {
      return ApprovalActionDto.fromJson(json['action'] as Map<String, dynamic>);
    }
    final id = JsonUtils.readInt(json, 'id', fallback: 0);
    final name = _firstNonEmpty(<String?>[
      _readString(json, 'name'),
      _readString(json, 'action_name'),
    ]);
    if (name == null) {
      throw const FormatException('결재 감사 로그 액션 이름이 누락되었습니다.');
    }
    final code = _firstNonEmpty(<String?>[
      _readString(json, 'code'),
      _readString(json, 'action_code'),
    ]);
    return ApprovalActionDto(id: id, name: name, code: code);
  }

  ApprovalAction toEntity() => ApprovalAction(id: id, name: name, code: code);
}

Map<String, dynamic>? _asMap(dynamic value) =>
    value is Map<String, dynamic> ? value : null;

String? _readString(
  Map<String, dynamic>? source,
  String key, {
  String? fallback,
}) {
  if (source == null) return fallback;
  final value = source[key];
  if (value is String) return value;
  if (value == null) return fallback;
  return value.toString();
}

DateTime? _parseDate(Object? value) {
  if (value == null) return null;
  if (value is DateTime) return value;
  if (value is String) return DateTime.tryParse(value);
  return null;
}

String? _firstNonEmpty(Iterable<String?> values) {
  for (final value in values) {
    if (value == null) {
      continue;
    }
    final trimmed = value.trim();
    if (trimmed.isNotEmpty) {
      return trimmed;
    }
  }
  return null;
}
