import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/common/utils/json_utils.dart';
import 'package:superport_v2/features/approvals/data/dtos/approval_dto.dart';

import '../../domain/entities/stock_transaction.dart';

/// 재고 트랜잭션 DTO
///
/// - API 응답(JSON)을 도메인 엔티티로 변환하고, 요청 페이로드를 구성한다.
class StockTransactionDto {
  StockTransactionDto({
    this.id,
    required this.transactionNo,
    required this.transactionDate,
    required this.type,
    required this.status,
    required this.warehouse,
    required this.createdBy,
    this.note,
    this.isActive = true,
    this.createdAt,
    this.updatedAt,
    this.lines = const [],
    this.customers = const [],
    this.approval,
    this.expectedReturnDate,
  });

  final int? id;
  final String transactionNo;
  final DateTime transactionDate;
  final StockTransactionType type;
  final StockTransactionStatus status;
  final StockTransactionWarehouse warehouse;
  final StockTransactionEmployee createdBy;
  final String? note;
  final bool isActive;
  final DateTime? createdAt;
  final DateTime? updatedAt;
  final List<StockTransactionLine> lines;
  final List<StockTransactionCustomer> customers;
  final ApprovalDto? approval;
  final DateTime? expectedReturnDate;

  /// JSON 객체를 DTO로 변환한다.
  factory StockTransactionDto.fromJson(Map<String, dynamic> json) {
    final typeJson = json['transaction_type'] as Map<String, dynamic>?;
    final statusJson = json['transaction_status'] as Map<String, dynamic>?;
    final warehouseJson = json['warehouse'] as Map<String, dynamic>?;
    final createdByJson = json['created_by'] as Map<String, dynamic>?;

    return StockTransactionDto(
      id: json['id'] as int?,
      transactionNo: json['transaction_no'] as String? ?? '',
      transactionDate: _parseDate(json['transaction_date']) ?? DateTime.now(),
      type: _parseType(typeJson, json),
      status: _parseStatus(statusJson, json),
      warehouse: _parseWarehouse(warehouseJson, json),
      createdBy: _parseEmployee(createdByJson, json),
      note: json['note'] as String?,
      isActive: (json['is_active'] as bool?) ?? true,
      createdAt: _parseDateTime(json['created_at']),
      updatedAt: _parseDateTime(json['updated_at']),
      lines: _parseLines(json),
      customers: _parseCustomers(json),
      approval: _parseApproval(json['approval']),
      expectedReturnDate:
          _parseDate(json['expected_return_date']) ??
          _parseDate(json['planned_return_date']) ??
          _parseDate(json['return_due_date']),
    );
  }

  /// 도메인 엔티티로 변환한다.
  StockTransaction toEntity() {
    return StockTransaction(
      id: id,
      transactionNo: transactionNo,
      transactionDate: transactionDate,
      type: type,
      status: status,
      warehouse: warehouse,
      createdBy: createdBy,
      note: note,
      isActive: isActive,
      createdAt: createdAt,
      updatedAt: updatedAt,
      lines: lines,
      customers: customers,
      approval: approval?.toEntity(),
      expectedReturnDate: expectedReturnDate,
    );
  }

  /// 페이지네이션 응답을 파싱한다.
  static PaginatedResult<StockTransaction> parsePaginated(dynamic json) {
    final raw = JsonUtils.extractList(json, keys: const ['items']);
    final items = raw
        .map(StockTransactionDto.fromJson)
        .map((dto) => dto.toEntity())
        .toList(growable: false);
    final map = json is Map<String, dynamic> ? json : <String, dynamic>{};
    return PaginatedResult<StockTransaction>(
      items: items,
      page: JsonUtils.readInt(map, 'page', fallback: 1),
      pageSize: JsonUtils.readInt(map, 'page_size', fallback: items.length),
      total: JsonUtils.readInt(map, 'total', fallback: items.length),
    );
  }

  /// 단건 응답을 파싱한다.
  static StockTransaction? parseSingle(dynamic json) {
    final map = JsonUtils.extractMap(json, keys: const ['data']);
    if (map.isEmpty) {
      return null;
    }
    return StockTransactionDto.fromJson(map).toEntity();
  }
}

StockTransactionType _parseType(
  Map<String, dynamic>? json,
  Map<String, dynamic> fallback,
) {
  final map = json ?? const <String, dynamic>{};
  final id = map['id'] as int? ?? fallback['transaction_type_id'] as int? ?? 0;
  final name =
      map['name'] as String? ??
      map['type_name'] as String? ??
      fallback['transaction_type_name'] as String? ??
      fallback['transaction_type'] as String? ??
      '';
  return StockTransactionType(id: id, name: name);
}

StockTransactionStatus _parseStatus(
  Map<String, dynamic>? json,
  Map<String, dynamic> fallback,
) {
  final map = json ?? const <String, dynamic>{};
  final id =
      map['id'] as int? ?? fallback['transaction_status_id'] as int? ?? 0;
  final name =
      map['name'] as String? ??
      map['status_name'] as String? ??
      fallback['transaction_status_name'] as String? ??
      fallback['transaction_status'] as String? ??
      '';
  return StockTransactionStatus(id: id, name: name);
}

StockTransactionWarehouse _parseWarehouse(
  Map<String, dynamic>? json,
  Map<String, dynamic> fallback,
) {
  final map = json ?? const <String, dynamic>{};
  final zipcodeMap = _mapOrEmpty(map['zipcode']);
  final fallbackZipcode = _mapOrEmpty(fallback['zipcode']);
  final mergedZipcode = zipcodeMap.isNotEmpty ? zipcodeMap : fallbackZipcode;
  return StockTransactionWarehouse(
    id: map['id'] as int? ?? fallback['warehouse_id'] as int? ?? 0,
    code:
        map['warehouse_code'] as String? ??
        fallback['warehouse_code'] as String? ??
        fallback['warehouseCode'] as String? ??
        '',
    name:
        map['warehouse_name'] as String? ??
        fallback['warehouse_name'] as String? ??
        fallback['warehouseName'] as String? ??
        '',
    zipcode:
        mergedZipcode['zipcode'] as String? ?? fallback['zipcode'] as String?,
    addressLine:
        mergedZipcode['road_name'] as String? ??
        mergedZipcode['roadName'] as String? ??
        fallback['address_line'] as String? ??
        fallback['addressLine'] as String?,
  );
}

StockTransactionEmployee _parseEmployee(
  Map<String, dynamic>? json,
  Map<String, dynamic> fallback,
) {
  final map = json ?? const <String, dynamic>{};
  return StockTransactionEmployee(
    id:
        map['id'] as int? ??
        fallback['created_by_id'] as int? ??
        fallback['created_by'] as int? ??
        0,
    employeeNo:
        map['employee_no'] as String? ??
        fallback['created_by_employee_no'] as String? ??
        '',
    name:
        map['employee_name'] as String? ??
        fallback['created_by_name'] as String? ??
        '',
  );
}

List<StockTransactionLine> _parseLines(Map<String, dynamic> json) {
  final raw = JsonUtils.extractList(json, keys: const ['lines']);
  return [
    for (final item in raw)
      StockTransactionLine(
        id: item['id'] as int?,
        lineNo: JsonUtils.readInt(item, 'line_no', fallback: 1),
        product: _parseProduct(item['product'] as Map<String, dynamic>?, item),
        quantity: _readQuantity(item['quantity']),
        unitPrice: _readDouble(item['unit_price']),
        note: item['note'] as String?,
      ),
  ];
}

StockTransactionProduct _parseProduct(
  Map<String, dynamic>? json,
  Map<String, dynamic> fallback,
) {
  final map = json ?? const <String, dynamic>{};
  return StockTransactionProduct(
    id:
        map['id'] as int? ??
        map['product_id'] as int? ??
        fallback['product_id'] as int? ??
        0,
    code:
        map['product_code'] as String? ??
        map['code'] as String? ??
        fallback['product_code'] as String? ??
        fallback['code'] as String? ??
        '',
    name:
        map['product_name'] as String? ??
        map['name'] as String? ??
        fallback['product_name'] as String? ??
        fallback['name'] as String? ??
        '',
    vendor: _parseVendor(map['vendor'], fallback),
    uom: _parseUom(map['uom'], fallback),
  );
}

StockTransactionVendorSummary? _parseVendor(
  dynamic source,
  Map<String, dynamic> fallback,
) {
  final map = _mapOrEmpty(source);
  final name =
      map['vendor_name'] as String? ??
      map['name'] as String? ??
      fallback['vendor_name'] as String? ??
      fallback['vendorName'] as String? ??
      fallback['manufacturer'] as String?;
  if (name == null || name.trim().isEmpty) {
    return null;
  }
  final id =
      map['id'] as int? ??
      map['vendor_id'] as int? ??
      fallback['vendor_id'] as int? ??
      0;
  return StockTransactionVendorSummary(id: id, name: name);
}

StockTransactionUomSummary? _parseUom(
  dynamic source,
  Map<String, dynamic> fallback,
) {
  final map = _mapOrEmpty(source);
  final name =
      map['uom_name'] as String? ??
      map['name'] as String? ??
      fallback['uom_name'] as String? ??
      fallback['uomName'] as String? ??
      fallback['unit'] as String?;
  if (name == null || name.trim().isEmpty) {
    return null;
  }
  final id =
      map['id'] as int? ??
      map['uom_id'] as int? ??
      fallback['uom_id'] as int? ??
      0;
  return StockTransactionUomSummary(id: id, name: name);
}

List<StockTransactionCustomer> _parseCustomers(Map<String, dynamic> json) {
  final raw = JsonUtils.extractList(json, keys: const ['customers']);
  return [
    for (final item in raw)
      StockTransactionCustomer(
        id: item['id'] as int?,
        customer: _parseCustomer(
          item['customer'] as Map<String, dynamic>?,
          item,
        ),
        note: item['note'] as String?,
      ),
  ];
}

StockTransactionCustomerSummary _parseCustomer(
  Map<String, dynamic>? json,
  Map<String, dynamic> fallback,
) {
  final map = json ?? const <String, dynamic>{};
  return StockTransactionCustomerSummary(
    id:
        map['id'] as int? ??
        map['customer_id'] as int? ??
        fallback['customer_id'] as int? ??
        0,
    code:
        map['customer_code'] as String? ??
        map['code'] as String? ??
        fallback['customer_code'] as String? ??
        fallback['code'] as String? ??
        '',
    name:
        map['customer_name'] as String? ??
        map['name'] as String? ??
        fallback['customer_name'] as String? ??
        fallback['name'] as String? ??
        '',
  );
}

ApprovalDto? _parseApproval(dynamic raw) {
  final map = _mapOrEmpty(raw);
  if (map.isEmpty) {
    return null;
  }
  try {
    return ApprovalDto.fromJson(map);
  } catch (_) {
    return null;
  }
}

Map<String, dynamic> _mapOrEmpty(dynamic value) =>
    value is Map<String, dynamic> ? value : const <String, dynamic>{};

int _readQuantity(Object? value) {
  if (value is int) {
    return value;
  }
  if (value is double) {
    return value.round();
  }
  if (value is String) {
    final parsedInt = int.tryParse(value);
    if (parsedInt != null) {
      return parsedInt;
    }
    final parsedDouble = double.tryParse(value);
    if (parsedDouble != null) {
      return parsedDouble.round();
    }
  }
  return 0;
}

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

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

double _readDouble(Object? value) {
  if (value is double) {
    return value;
  }
  if (value is int) {
    return value.toDouble();
  }
  if (value is String) {
    return double.tryParse(value) ?? 0;
  }
  return 0;
}
