import 'package:collection/collection.dart';

/// 受注ステータスの定義。
enum SalesOrderStatus {
  draft,
  confirmed,
  picking,
  shipped,
  closed,
  cancelled,
}

extension SalesOrderStatusX on SalesOrderStatus {
  String get displayName {
    switch (this) {
      case SalesOrderStatus.draft:
        return '下書き';
      case SalesOrderStatus.confirmed:
        return '確定';
      case SalesOrderStatus.picking:
        return '出荷準備中';
      case SalesOrderStatus.shipped:
        return '出荷済み';
      case SalesOrderStatus.closed:
        return '完了';
      case SalesOrderStatus.cancelled:
        return 'キャンセル';
    }
  }

  static SalesOrderStatus fromDbValue(String? value) {
    return SalesOrderStatus.values.firstWhere(
      (status) => status.name == value,
      orElse: () => SalesOrderStatus.draft,
    );
  }
}

class SalesOrderItem {
  SalesOrderItem({
    required this.id,
    required this.orderId,
    required this.description,
    required this.quantity,
    required this.unitPrice,
    this.productId,
    this.taxRate = 0,
    this.sortIndex = 0,
  });

  final String id;
  final String orderId;
  final String? productId;
  final String description;
  final int quantity;
  final int unitPrice;
  final double taxRate;
  final int sortIndex;

  int get lineTotal => quantity * unitPrice;

  Map<String, dynamic> toMap() => {
        'id': id,
        'order_id': orderId,
        'product_id': productId,
        'description': description,
        'quantity': quantity,
        'unit_price': unitPrice,
        'tax_rate': taxRate,
        'sort_index': sortIndex,
      };

  factory SalesOrderItem.fromMap(Map<String, dynamic> map) => SalesOrderItem(
        id: map['id'] as String,
        orderId: map['order_id'] as String,
        productId: map['product_id'] as String?,
        description: map['description'] as String,
        quantity: map['quantity'] as int,
        unitPrice: map['unit_price'] as int,
        taxRate: (map['tax_rate'] as num?)?.toDouble() ?? 0,
        sortIndex: map['sort_index'] as int? ?? 0,
      );
}

class SalesOrder {
  SalesOrder({
    required this.id,
    required this.customerId,
    required this.orderDate,
    required this.status,
    required this.subtotal,
    required this.taxAmount,
    required this.totalAmount,
    required this.createdAt,
    required this.updatedAt,
    this.orderNumber,
    this.customerNameSnapshot,
    this.requestedShipDate,
    this.notes,
    this.assignedTo,
    this.workflowStage,
    this.items = const [],
  });

  final String id;
  final String customerId;
  final String? customerNameSnapshot;
  final DateTime orderDate;
  final DateTime? requestedShipDate;
  final SalesOrderStatus status;
  final int subtotal;
  final int taxAmount;
  final int totalAmount;
  final String? orderNumber;
  final String? notes;
  final String? assignedTo;
  final String? workflowStage;
  final DateTime createdAt;
  final DateTime updatedAt;
  final List<SalesOrderItem> items;

  SalesOrder copyWith({
    String? id,
    String? customerId,
    String? customerNameSnapshot,
    DateTime? orderDate,
    DateTime? requestedShipDate,
    SalesOrderStatus? status,
    int? subtotal,
    int? taxAmount,
    int? totalAmount,
    String? orderNumber,
    String? notes,
    String? assignedTo,
    String? workflowStage,
    DateTime? createdAt,
    DateTime? updatedAt,
    List<SalesOrderItem>? items,
  }) {
    return SalesOrder(
      id: id ?? this.id,
      customerId: customerId ?? this.customerId,
      customerNameSnapshot: customerNameSnapshot ?? this.customerNameSnapshot,
      orderDate: orderDate ?? this.orderDate,
      requestedShipDate: requestedShipDate ?? this.requestedShipDate,
      status: status ?? this.status,
      subtotal: subtotal ?? this.subtotal,
      taxAmount: taxAmount ?? this.taxAmount,
      totalAmount: totalAmount ?? this.totalAmount,
      orderNumber: orderNumber ?? this.orderNumber,
      notes: notes ?? this.notes,
      assignedTo: assignedTo ?? this.assignedTo,
      workflowStage: workflowStage ?? this.workflowStage,
      createdAt: createdAt ?? this.createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
      items: items ?? this.items,
    );
  }

  Map<String, dynamic> toMap() => {
        'id': id,
        'order_number': orderNumber,
        'customer_id': customerId,
        'customer_name_snapshot': customerNameSnapshot,
        'order_date': orderDate.toIso8601String(),
        'requested_ship_date': requestedShipDate?.toIso8601String(),
        'status': status.name,
        'subtotal': subtotal,
        'tax_amount': taxAmount,
        'total_amount': totalAmount,
        'notes': notes,
        'assigned_to': assignedTo,
        'workflow_stage': workflowStage,
        'created_at': createdAt.toIso8601String(),
        'updated_at': updatedAt.toIso8601String(),
      };

  factory SalesOrder.fromMap(Map<String, dynamic> map, {List<SalesOrderItem> items = const []}) {
    return SalesOrder(
      id: map['id'] as String,
      orderNumber: map['order_number'] as String?,
      customerId: map['customer_id'] as String,
      customerNameSnapshot: map['customer_name_snapshot'] as String?,
      orderDate: DateTime.parse(map['order_date'] as String),
      requestedShipDate: (map['requested_ship_date'] as String?) != null
          ? DateTime.parse(map['requested_ship_date'] as String)
          : null,
      status: SalesOrderStatusX.fromDbValue(map['status'] as String?),
      subtotal: map['subtotal'] as int? ?? 0,
      taxAmount: map['tax_amount'] as int? ?? 0,
      totalAmount: map['total_amount'] as int? ?? 0,
      notes: map['notes'] as String?,
      assignedTo: map['assigned_to'] as String?,
      workflowStage: map['workflow_stage'] as String?,
      createdAt: DateTime.parse(map['created_at'] as String),
      updatedAt: DateTime.parse(map['updated_at'] as String),
      items: items,
    );
  }

  SalesOrder recalculateTotals({double? defaultTaxRate}) {
    final newSubtotal = items.fold<int>(0, (sum, item) => sum + item.lineTotal);
    final taxRate = defaultTaxRate ?? items.map((e) => e.taxRate).firstWhereOrNull((rate) => rate > 0) ?? 0;
    final newTaxAmount = (newSubtotal * taxRate).round();
    return copyWith(
      subtotal: newSubtotal,
      taxAmount: newTaxAmount,
      totalAmount: newSubtotal + newTaxAmount,
    );
  }
}
