import 'package:uuid/uuid.dart';

import '../models/order_models.dart';
import 'company_profile_service.dart';
import 'sales_order_repository.dart';

class SalesOrderLineInput {
  const SalesOrderLineInput({
    required this.description,
    required this.quantity,
    required this.unitPrice,
    this.productId,
    this.taxRate,
  });

  final String description;
  final int quantity;
  final int unitPrice;
  final String? productId;
  final double? taxRate;
}

class SalesOrderService {
  SalesOrderService({
    SalesOrderRepository? repository,
    CompanyProfileService? companyProfileService,
  })  : _repository = repository ?? SalesOrderRepository(),
        _companyProfileService = companyProfileService ?? CompanyProfileService();

  final SalesOrderRepository _repository;
  final CompanyProfileService _companyProfileService;
  final Uuid _uuid = const Uuid();

  static const Map<SalesOrderStatus, List<SalesOrderStatus>> _transitions = {
    SalesOrderStatus.draft: [SalesOrderStatus.confirmed, SalesOrderStatus.cancelled],
    SalesOrderStatus.confirmed: [SalesOrderStatus.picking, SalesOrderStatus.cancelled],
    SalesOrderStatus.picking: [SalesOrderStatus.shipped, SalesOrderStatus.cancelled],
    SalesOrderStatus.shipped: [SalesOrderStatus.closed],
    SalesOrderStatus.closed: [],
    SalesOrderStatus.cancelled: [],
  };

  Future<List<SalesOrder>> fetchOrders({SalesOrderStatus? status}) {
    return _repository.fetchOrders(status: status);
  }

  Future<SalesOrder> createOrder({
    required String customerId,
    required String customerName,
    List<SalesOrderLineInput> lines = const [],
    DateTime? requestedShipDate,
    String? notes,
    String? assignedTo,
  }) async {
    final profile = await _companyProfileService.loadProfile();
    final now = DateTime.now();
    final orderId = _uuid.v4();
    final lineItems = _buildItems(orderId, lines);
    final order = SalesOrder(
      id: orderId,
      orderNumber: _generateOrderNumber(now),
      customerId: customerId,
      customerNameSnapshot: customerName,
      orderDate: now,
      requestedShipDate: requestedShipDate,
      status: SalesOrderStatus.draft,
      subtotal: 0,
      taxAmount: 0,
      totalAmount: 0,
      notes: notes,
      assignedTo: assignedTo,
      workflowStage: _workflowStage(SalesOrderStatus.draft),
      createdAt: now,
      updatedAt: now,
      items: lineItems,
    ).recalculateTotals(defaultTaxRate: profile.taxRate);

    await _repository.upsertOrder(order);
    return order;
  }

  Future<SalesOrder> updateOrder(
    SalesOrder order, {
    List<SalesOrderLineInput>? replacedLines,
    DateTime? requestedShipDate,
    String? notes,
    String? assignedTo,
  }) async {
    final profile = await _companyProfileService.loadProfile();
    final now = DateTime.now();
    final nextItems = replacedLines != null ? _buildItems(order.id, replacedLines) : order.items;
    final updated = order
        .copyWith(
          requestedShipDate: requestedShipDate ?? order.requestedShipDate,
          notes: notes ?? order.notes,
          assignedTo: assignedTo ?? order.assignedTo,
          updatedAt: now,
          items: nextItems,
        )
        .recalculateTotals(defaultTaxRate: profile.taxRate);
    await _repository.upsertOrder(updated);
    return updated;
  }

  Future<SalesOrder> transitionStatus(String orderId, SalesOrderStatus nextStatus, {bool force = false}) async {
    final order = await _repository.findById(orderId);
    if (order == null) {
      throw StateError('order not found: $orderId');
    }
    if (!force && !_canTransition(order.status, nextStatus)) {
      throw StateError('invalid transition ${order.status.name} -> ${nextStatus.name}');
    }
    final now = DateTime.now();
    final updated = order.copyWith(
      status: nextStatus,
      workflowStage: _workflowStage(nextStatus),
      updatedAt: now,
    );
    await _repository.upsertOrder(updated);
    return updated;
  }

  Future<SalesOrder> advanceStatus(String orderId) async {
    final order = await _repository.findById(orderId);
    if (order == null) {
      throw StateError('order not found: $orderId');
    }
    final candidates = _transitions[order.status];
    if (candidates == null || candidates.isEmpty) {
      return order;
    }
    return transitionStatus(orderId, candidates.first);
  }

  bool _canTransition(SalesOrderStatus current, SalesOrderStatus next) {
    final allowed = _transitions[current];
    return allowed?.contains(next) ?? false;
  }

  List<SalesOrderStatus> nextStatuses(SalesOrderStatus current) {
    return List.unmodifiable(_transitions[current] ?? const []);
  }

  List<SalesOrderItem> _buildItems(String orderId, List<SalesOrderLineInput> lines) {
    return lines.asMap().entries.map((entry) {
      final index = entry.key;
      final line = entry.value;
      return SalesOrderItem(
        id: _uuid.v4(),
        orderId: orderId,
        productId: line.productId,
        description: line.description,
        quantity: line.quantity,
        unitPrice: line.unitPrice,
        taxRate: line.taxRate ?? 0,
        sortIndex: index,
      );
    }).toList();
  }

  String _generateOrderNumber(DateTime timestamp) {
    final datePart = '${timestamp.year}${timestamp.month.toString().padLeft(2, '0')}${timestamp.day.toString().padLeft(2, '0')}';
    final timePart = '${timestamp.hour.toString().padLeft(2, '0')}${timestamp.minute.toString().padLeft(2, '0')}';
    return 'SO$datePart-$timePart-${timestamp.millisecondsSinceEpoch % 1000}'.toUpperCase();
  }

  String _workflowStage(SalesOrderStatus status) {
    switch (status) {
      case SalesOrderStatus.draft:
        return 'order';
      case SalesOrderStatus.confirmed:
        return 'ready';
      case SalesOrderStatus.picking:
        return 'picking';
      case SalesOrderStatus.shipped:
        return 'shipping';
      case SalesOrderStatus.closed:
        return 'closed';
      case SalesOrderStatus.cancelled:
        return 'cancelled';
    }
  }
}
