import 'cargo_item.dart';
import 'delivery_station.dart';
import 'task.dart';

class Job {
  final String id; // Will store the timestamp as string
  final String jobNumber;
  final String status;
  final DateTime createdAt;
  final DateTime updatedAt;
  final String createdBy;
  final String customerSelection;
  final String pickupCompany;
  final String? pickupSalutation;
  final String pickupFirstName;
  final String pickupLastName;
  final String pickupPhone;
  final String pickupStreet;
  final String pickupHouseNumber;
  final String pickupAddressAddition;
  final String pickupZip;
  final String pickupCity;
  final String deliveryCompany;
  final String? deliverySalutation;
  final String deliveryFirstName;
  final String deliveryLastName;
  final String deliveryPhone;
  final String deliveryStreet;
  final String deliveryHouseNumber;
  final String deliveryAddressAddition;
  final String deliveryZip;
  final String deliveryCity;
  final bool digitalProcessing;
  final String appUser;
  final String pickupDate;
  final String pickupTime;
  final String deliveryDate;
  final String deliveryTime;
  final String remark;
  final double price;
  final bool draft;

  // New fields for cargoItems and tasks
  final List<CargoItem> cargoItems;
  final List<DeliveryStation> deliveryStations;
  final List<Task> tasks;
  final String deliveryCitiesDisplay;
  final String firstDeliveryCity;
  final String lastDeliveryCity;

  // Legacy fields for backward compatibility
  final String title;
  final String description;
  final String priority;
  final DateTime? dueDate;
  final String? assignedTo;
  final String? location;
  final Map<String, dynamic>? additionalData;

  Job({
    required this.id,
    required this.jobNumber,
    required this.status,
    required this.createdAt,
    required this.updatedAt,
    required this.createdBy,
    required this.customerSelection,
    required this.pickupCompany,
    this.pickupSalutation,
    required this.pickupFirstName,
    required this.pickupLastName,
    required this.pickupPhone,
    required this.pickupStreet,
    required this.pickupHouseNumber,
    required this.pickupAddressAddition,
    required this.pickupZip,
    required this.pickupCity,
    required this.deliveryCompany,
    this.deliverySalutation,
    required this.deliveryFirstName,
    required this.deliveryLastName,
    required this.deliveryPhone,
    required this.deliveryStreet,
    required this.deliveryHouseNumber,
    required this.deliveryAddressAddition,
    required this.deliveryZip,
    required this.deliveryCity,
    required this.digitalProcessing,
    required this.appUser,
    required this.pickupDate,
    required this.pickupTime,
    required this.deliveryDate,
    required this.deliveryTime,
    required this.remark,
    required this.price,
    required this.draft,
    // New fields for cargoItems and tasks
    required this.cargoItems,
    required this.tasks,
    this.deliveryStations = const [],
    this.deliveryCitiesDisplay = '',
    this.firstDeliveryCity = '',
    this.lastDeliveryCity = '',
    // Legacy fields
    String? title,
    String? description,
    this.priority = 'normal',
    this.dueDate,
    this.assignedTo,
    this.location,
    this.additionalData,
  }) : title = title ?? 'Job $jobNumber',
       description =
           description ?? 'Transport von $pickupCity nach $deliveryCity';

  /// Parse DateTime from either string or array format
  static DateTime? _parseDateTime(dynamic value) {
    if (value == null) return null;

    if (value is String) {
      return DateTime.tryParse(value);
    }

    if (value is List && value.isNotEmpty) {
      try {
        // Array format: [year, month, day, hour, minute, second, nanosecond]
        final year = value[0] as int;
        final month = value.length > 1 ? value[1] as int : 1;
        final day = value.length > 2 ? value[2] as int : 1;
        final hour = value.length > 3 ? value[3] as int : 0;
        final minute = value.length > 4 ? value[4] as int : 0;
        final second = value.length > 5 ? value[5] as int : 0;
        final nanosecond = value.length > 6 ? value[6] as int : 0;

        // Convert nanoseconds to microseconds (divide by 1000)
        final microsecond = nanosecond ~/ 1000;

        return DateTime(year, month, day, hour, minute, second, microsecond);
      } catch (e) {
        return null;
      }
    }

    return null;
  }

  /// Parse time string (for pickupTime/deliveryTime)
  static String? _parseTimeString(dynamic value) {
    if (value == null) return null;
    if (value is String && value.isNotEmpty && value != 'null') {
      return value;
    }
    return null;
  }

  /// Parse date string from either string or array format (for pickupDate/deliveryDate)
  static String? _parseDateString(dynamic value) {
    if (value == null) return null;

    if (value is String) {
      return value;
    }

    if (value is List && value.isNotEmpty) {
      try {
        // Array format: [year, month, day]
        final year = value[0] as int;
        final month = value.length > 1 ? value[1] as int : 1;
        final day = value.length > 2 ? value[2] as int : 1;

        // Format as ISO date string
        return '${year.toString().padLeft(4, '0')}-${month.toString().padLeft(2, '0')}-${day.toString().padLeft(2, '0')}';
      } catch (e) {
        return null;
      }
    }

    return value.toString();
  }

  static String _readString(dynamic value, {String fallback = ''}) {
    if (value is String) {
      return value;
    }
    if (value is num || value is bool) {
      return value.toString();
    }
    return fallback;
  }

  factory Job.fromJson(Map<String, dynamic> json) {
    // Support both flat structure and { job: {...}, cargoItems: [...], tasks: [...] }
    final jobJson =
        (json['job'] is Map)
            ? Map<String, dynamic>.from(json['job'] as Map)
            : json;

    // Determine the id robustly. Prefer the inner job.id if present.
    String idValue = '';
    final dynamic innerId = jobJson['id'];
    if (innerId is Map<String, dynamic>) {
      // Some backends send an object; try common fields
      idValue =
          innerId['timestamp']?.toString() ??
          innerId[r'$oid']?.toString() ??
          '';
    } else if (innerId != null) {
      idValue = innerId.toString();
    }
    if (idValue.isEmpty) {
      // Fallback to outer json['id'] if provided
      final dynamic outerId = json['id'];
      if (outerId is Map<String, dynamic>) {
        idValue =
            outerId['timestamp']?.toString() ??
            outerId[r'$oid']?.toString() ??
            '';
      } else if (outerId != null) {
        idValue = outerId.toString();
      }
    }

    // Parse cargoItems array
    List<CargoItem> cargoItems = [];
    if (json['cargoItems'] is List) {
      cargoItems =
          (json['cargoItems'] as List)
              .map(
                (item) =>
                    CargoItem.fromJson(Map<String, dynamic>.from(item as Map)),
              )
              .toList();
    }

    // Parse delivery stations and prefer their tasks over the legacy top-level tasks.
    List<DeliveryStation> deliveryStations = [];
    final deliveryStationsRaw =
        jobJson['deliveryStations'] ?? json['deliveryStations'];
    if (deliveryStationsRaw is List) {
      deliveryStations =
          deliveryStationsRaw
              .map(
                (station) => DeliveryStation.fromJson(
                  Map<String, dynamic>.from(station as Map),
                ),
              )
              .toList()
            ..sort((a, b) => a.stationOrder.compareTo(b.stationOrder));
    }

    int compareTasks(Task a, Task b) {
      final stationCompare = (a.stationOrder ?? -1).compareTo(
        b.stationOrder ?? -1,
      );
      if (stationCompare != 0) {
        return stationCompare;
      }
      return (a.taskOrder ?? 0).compareTo(b.taskOrder ?? 0);
    }

    // The tasks embedded in deliveryStations are snapshots from job creation
    // and never reflect later completions. The top-level tasks list comes from
    // the server's task collection and carries the current completion state,
    // so overlay it onto the station tasks by id.
    final topLevelTasksRaw = json['tasks'] ?? jobJson['tasks'];
    if (deliveryStations.isNotEmpty && topLevelTasksRaw is List) {
      final freshTasksById = <String, Task>{};
      for (final rawTask in topLevelTasksRaw) {
        if (rawTask is! Map) {
          continue;
        }
        try {
          final task = Task.fromJson(Map<String, dynamic>.from(rawTask));
          if (task.id.isNotEmpty) {
            freshTasksById[task.id] = task;
          }
        } catch (_) {
          // Ignore unparseable entries; the station snapshot stays in place.
        }
      }
      for (final station in deliveryStations) {
        for (var i = 0; i < station.tasks.length; i++) {
          final fresh = freshTasksById[station.tasks[i].id];
          if (fresh != null && fresh.completed && !station.tasks[i].completed) {
            station.tasks[i] = station.tasks[i].copyWith(
              completed: true,
              completedAt: fresh.completedAt,
              completedBy: fresh.completedBy,
            );
          }
        }
      }
    }

    // Parse tasks array
    List<Task> tasks = [];
    if (deliveryStations.isNotEmpty) {
      tasks =
          deliveryStations.expand((station) => station.tasks).toList()
            ..sort(compareTasks);
    } else if (json['tasks'] is List) {
      tasks =
          (json['tasks'] as List)
              .map(
                (task) => Task.fromJson(Map<String, dynamic>.from(task as Map)),
              )
              .toList()
            ..sort(compareTasks);
    } else if (jobJson['tasks'] is List) {
      tasks =
          (jobJson['tasks'] as List)
              .map(
                (task) => Task.fromJson(Map<String, dynamic>.from(task as Map)),
              )
              .toList()
            ..sort(compareTasks);
    }

    // As a last resort, derive a deterministic id if still empty (avoid UNIQUE '' collisions)
    if (idValue.isEmpty) {
      final jobNumber = jobJson['jobNumber']?.toString();
      final createdAt = jobJson['createdAt']?.toString();
      idValue =
          (jobNumber?.isNotEmpty == true)
              ? 'jobnum:$jobNumber'
              : (createdAt?.isNotEmpty == true
                  ? 'ts:${createdAt!}'
                  : DateTime.now().millisecondsSinceEpoch.toString());
    }

    return Job(
      id: idValue,
      jobNumber: jobJson['jobNumber']?.toString() ?? '',
      status: jobJson['status']?.toString() ?? 'UNKNOWN',
      createdAt: _parseDateTime(jobJson['createdAt']) ?? DateTime.now(),
      updatedAt: _parseDateTime(jobJson['updatedAt']) ?? DateTime.now(),
      createdBy: jobJson['createdBy']?.toString() ?? '',
      customerSelection: jobJson['customerSelection']?.toString() ?? '',
      pickupCompany: jobJson['pickupCompany']?.toString() ?? '',
      pickupSalutation: jobJson['pickupSalutation']?.toString(),
      pickupFirstName: jobJson['pickupFirstName']?.toString() ?? '',
      pickupLastName: jobJson['pickupLastName']?.toString() ?? '',
      pickupPhone: jobJson['pickupPhone']?.toString() ?? '',
      pickupStreet: jobJson['pickupStreet']?.toString() ?? '',
      pickupHouseNumber: jobJson['pickupHouseNumber']?.toString() ?? '',
      pickupAddressAddition: jobJson['pickupAddressAddition']?.toString() ?? '',
      pickupZip: jobJson['pickupZip']?.toString() ?? '',
      pickupCity: jobJson['pickupCity']?.toString() ?? '',
      deliveryCompany: jobJson['deliveryCompany']?.toString() ?? '',
      deliverySalutation: jobJson['deliverySalutation']?.toString(),
      deliveryFirstName: jobJson['deliveryFirstName']?.toString() ?? '',
      deliveryLastName: jobJson['deliveryLastName']?.toString() ?? '',
      deliveryPhone: jobJson['deliveryPhone']?.toString() ?? '',
      deliveryStreet: jobJson['deliveryStreet']?.toString() ?? '',
      deliveryHouseNumber: jobJson['deliveryHouseNumber']?.toString() ?? '',
      deliveryAddressAddition:
          jobJson['deliveryAddressAddition']?.toString() ?? '',
      deliveryZip: jobJson['deliveryZip']?.toString() ?? '',
      deliveryCity: jobJson['deliveryCity']?.toString() ?? '',
      digitalProcessing: jobJson['digitalProcessing'] == true,
      appUser: jobJson['appUser']?.toString() ?? '',
      pickupDate: _parseDateString(jobJson['pickupDate']) ?? '',
      pickupTime: _parseTimeString(jobJson['pickupTime']) ?? '',
      deliveryDate: _parseDateString(jobJson['deliveryDate']) ?? '',
      deliveryTime: _parseTimeString(jobJson['deliveryTime']) ?? '',
      remark: _readString(jobJson['remark']),
      price: (jobJson['price'] is num) ? jobJson['price'].toDouble() : 0.0,
      draft: jobJson['draft'] == true,
      // New fields for cargoItems and tasks
      cargoItems: cargoItems,
      deliveryStations: deliveryStations,
      tasks: tasks,
      deliveryCitiesDisplay: _readString(jobJson['deliveryCitiesDisplay']),
      firstDeliveryCity: _readString(jobJson['firstDeliveryCity']),
      lastDeliveryCity: _readString(jobJson['lastDeliveryCity']),
      // Legacy fields for backward compatibility
      title: jobJson['title']?.toString(),
      description: jobJson['description']?.toString(),
      priority: jobJson['priority']?.toString() ?? 'normal',
      dueDate: _parseDateTime(jobJson['dueDate']),
      assignedTo: jobJson['assignedTo']?.toString(),
      location: jobJson['location']?.toString(),
      additionalData: jobJson['additionalData'] as Map<String, dynamic>?,
    );
  }

  /// Return a normalized copy (trim strings, null→'')
  Job normalized() {
    String t(String? s) => (s ?? '').trim();
    return Job(
      id: id,
      jobNumber: t(jobNumber),
      status: t(status),
      createdAt: createdAt,
      updatedAt: updatedAt,
      createdBy: t(createdBy),
      customerSelection: t(customerSelection),
      pickupCompany: t(pickupCompany),
      pickupSalutation: t(pickupSalutation),
      pickupFirstName: t(pickupFirstName),
      pickupLastName: t(pickupLastName),
      pickupPhone: t(pickupPhone),
      pickupStreet: t(pickupStreet),
      pickupHouseNumber: t(pickupHouseNumber),
      pickupAddressAddition: t(pickupAddressAddition),
      pickupZip: t(pickupZip),
      pickupCity: t(pickupCity),
      deliveryCompany: t(deliveryCompany),
      deliverySalutation: t(deliverySalutation),
      deliveryFirstName: t(deliveryFirstName),
      deliveryLastName: t(deliveryLastName),
      deliveryPhone: t(deliveryPhone),
      deliveryStreet: t(deliveryStreet),
      deliveryHouseNumber: t(deliveryHouseNumber),
      deliveryAddressAddition: t(deliveryAddressAddition),
      deliveryZip: t(deliveryZip),
      deliveryCity: t(deliveryCity),
      digitalProcessing: digitalProcessing,
      appUser: t(appUser),
      pickupDate: t(pickupDate),
      pickupTime: t(pickupTime),
      deliveryDate: t(deliveryDate),
      deliveryTime: t(deliveryTime),
      remark: t(remark),
      price: price,
      draft: draft,
      cargoItems: cargoItems,
      deliveryStations:
          deliveryStations.map((station) => station.normalized()).toList(),
      tasks: tasks,
      deliveryCitiesDisplay: t(deliveryCitiesDisplay),
      firstDeliveryCity: t(firstDeliveryCity),
      lastDeliveryCity: t(lastDeliveryCity),
      title: t(title),
      description: t(description),
      priority: t(priority),
      dueDate: dueDate,
      assignedTo: t(assignedTo),
      location: t(location),
      additionalData: additionalData,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'jobNumber': jobNumber,
      'status': status,
      'createdAt': createdAt.toIso8601String(),
      'updatedAt': updatedAt.toIso8601String(),
      'createdBy': createdBy,
      'customerSelection': customerSelection,
      'pickupCompany': pickupCompany,
      'pickupSalutation': pickupSalutation,
      'pickupFirstName': pickupFirstName,
      'pickupLastName': pickupLastName,
      'pickupPhone': pickupPhone,
      'pickupStreet': pickupStreet,
      'pickupHouseNumber': pickupHouseNumber,
      'pickupAddressAddition': pickupAddressAddition,
      'pickupZip': pickupZip,
      'pickupCity': pickupCity,
      'deliveryCompany': deliveryCompany,
      'deliverySalutation': deliverySalutation,
      'deliveryFirstName': deliveryFirstName,
      'deliveryLastName': deliveryLastName,
      'deliveryPhone': deliveryPhone,
      'deliveryStreet': deliveryStreet,
      'deliveryHouseNumber': deliveryHouseNumber,
      'deliveryAddressAddition': deliveryAddressAddition,
      'deliveryZip': deliveryZip,
      'deliveryCity': deliveryCity,
      'digitalProcessing': digitalProcessing,
      'appUser': appUser,
      'pickupDate': pickupDate,
      'pickupTime': pickupTime,
      'deliveryDate': deliveryDate,
      'deliveryTime': deliveryTime,
      'remark': remark,
      'price': price,
      'draft': draft,
      // New fields for cargoItems and tasks
      'cargoItems': cargoItems.map((item) => item.toJson()).toList(),
      'deliveryStations':
          deliveryStations.map((station) => station.toJson()).toList(),
      'tasks': tasks.map((task) => task.toJson()).toList(),
      'deliveryCitiesDisplay': deliveryCitiesDisplay,
      'firstDeliveryCity': firstDeliveryCity,
      'lastDeliveryCity': lastDeliveryCity,
      // Legacy fields
      'title': title,
      'description': description,
      'priority': priority,
      'dueDate': dueDate?.toIso8601String(),
      'assignedTo': assignedTo,
      'location': location,
      'additionalData': additionalData,
    };
  }

  @override
  String toString() {
    return 'Job(id: $id, title: $title, status: $status, priority: $priority)';
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is Job && other.id == id;
  }

  @override
  int get hashCode => id.hashCode;

  /// Get status color for UI display
  String get statusColor {
    switch (status.toLowerCase()) {
      case 'created':
        return 'orange';
      case 'pending':
      case 'assigned':
        return 'orange';
      case 'in_progress':
      case 'started':
        return 'blue';
      case 'completed':
      case 'done':
        return 'green';
      case 'cancelled':
      case 'failed':
        return 'red';
      default:
        return 'grey';
    }
  }

  /// Get status display text in German
  String get statusDisplayText {
    switch (status.toLowerCase()) {
      case 'created':
        return 'Erstellt';
      case 'pending':
        return 'Wartend';
      case 'assigned':
        return 'Zugewiesen';
      case 'in_progress':
      case 'started':
        return 'In Bearbeitung';
      case 'completed':
      case 'done':
        return 'Abgeschlossen';
      case 'cancelled':
        return 'Abgebrochen';
      case 'failed':
        return 'Fehlgeschlagen';
      default:
        return status; // Show the original status if not mapped
    }
  }

  /// Get priority display text in German
  String get priorityDisplayText {
    switch (priority.toLowerCase()) {
      case 'low':
        return 'Niedrig';
      case 'normal':
        return 'Normal';
      case 'high':
        return 'Hoch';
      case 'urgent':
        return 'Dringend';
      default:
        return 'Normal';
    }
  }
}
