import 'package:logger/logger.dart';
import 'package:sigap/src/features/daily-ops/data/models/models/officers_model.dart';
import 'package:sigap/src/features/personalization/data/models/index.dart';

/// A model representing user metadata with a focus on single source of truth
/// by delegating properties to related models when possible
class UserMetadataModel {
  // Core properties that define the user type
  final bool isOfficer;
  final String? userId;
  final String? roleId; 
  final String profileStatus;

  // Related models that hold specific data
  final OfficerModel? officerData;
  final UserModel? viewerData;

  // Only store properties here that aren't available in related models
  final String? email;
  final Map<String, dynamic>? additionalData;
  
  const UserMetadataModel({
    this.isOfficer = false,
    this.userId,
    this.roleId,
    this.profileStatus = 'incomplete',
    this.email,
    this.officerData,
    this.viewerData,
    this.additionalData,
  });

  /// Create UserMetadataModel from raw Map data (from Supabase Auth)
  factory UserMetadataModel.fromJson(Map<String, dynamic>? json) {
    if (json == null || json.isEmpty) {
      return const UserMetadataModel();
    }
    
    final bool isOfficer = json['is_officer'] == true;

    // Parse officer data
    OfficerModel? officerData;
    if (json['officer_data'] != null && isOfficer) {
      try {
        final officerJson = Map<String, dynamic>.from(json['officer_data']);
        
        // Only add missing required fields, allow null for optional ones
        officerJson.putIfAbsent('id', () => json['id']);
        officerJson.putIfAbsent('role_id', () => null);
        officerJson.putIfAbsent('unit_id', () => null);

        officerData = OfficerModel.fromJson(officerJson);
      } catch (e) {
        Logger().e('Failed to parse officer data: $e');
      }
    }

    // Parse profile data
    UserModel? viewerData;
    if (json['profile_data'] != null && !isOfficer) {
      try {
        final profileJson = Map<String, dynamic>.from(json['profile_data']);
        
        profileJson.putIfAbsent('id', () => null);
        profileJson.putIfAbsent('user_id', () => json['id']);
        profileJson.putIfAbsent('nik', () => json['nik']);

        viewerData = UserModel.fromJson(profileJson);
      } catch (e) {
        Logger().e('Failed to parse profile data: $e');
      }
    }
    
    // Create additionalData by excluding known fields
    final excludedKeys = {
      'is_officer',
      'user_id',
      'role_id',
      'email',
      'officer_data',
      'profile_data',
      'emergency_contact',
    };

    final additionalData = Map<String, dynamic>.from(json)
      ..removeWhere((key, _) => excludedKeys.contains(key));

    return UserMetadataModel(
      isOfficer: isOfficer,
      userId: json['user_id'] as String?,
      roleId: json['role_id'] ?? json['initial_role_id'] as String?,
      profileStatus: json['profile_status'] as String? ?? 'incomplete',
      email: json['email'] as String?,
      officerData: officerData,
      viewerData: viewerData,
      additionalData: additionalData.isNotEmpty ? additionalData : null,
    );
  }

  /// Convert model to JSON Map for Supabase Auth
  Map<String, dynamic> toJson() {
    final data = <String, dynamic>{
      'is_officer': isOfficer,
      'profile_status': profileStatus,
    };

    // Add basic user data
    if (roleId != null) data['role_id'] = roleId;
    if (userId != null) data['user_id'] = userId;
    if (email != null) data['email'] = email;

    // Add officer-specific data
    if (officerData != null && isOfficer) {
      data['officer_data'] = officerData!.toJson();
    }
    
    // Add profile data for non-officers
    if (viewerData != null && !isOfficer) {
      data['profile_data'] = viewerData!.toJson();
    }

    // Add additional data
    if (additionalData != null) {
      data.addAll(additionalData!);
    }

    return data;
  }
  
  /// Convert to specialized JSON for initial signup
  Map<String, dynamic> toInitialSignupJson() {
    return {
      'is_officer': isOfficer,
      if (email != null) 'email': email,
      if (name != null) 'name': name,
      if (phone != null) 'phone': phone,
      'profile_status': profileStatus,
      'role_id': roleId,
    };
  }

  /// Convert to specialized JSON for profile completion
  Map<String, dynamic> toProfileCompletionJson() {
    return {
      'profile_status': 'completed',
      'is_officer': isOfficer,
      if (name != null) 'name': name,
      if (phone != null) 'phone': phone,
      if (roleId != null) 'role_id': roleId,
    };
  }

  /// Converts to JSON format for auth metadata update
  Map<String, dynamic> toAuthMetadataJson() {
    return {
      'is_officer': isOfficer,
      'role_id': roleId,
      'profile_status': profileStatus,
      if (name != null) 'name': name,
    };
  }

  factory UserMetadataModel.fromInitUserMetadata(
    UserMetadataModel metadata, {
    String? email,
    String profileStatus = 'incomplete',
  }) {
    return UserMetadataModel(
      email: email,
      isOfficer: metadata.isOfficer,
      roleId: metadata.roleId,
      profileStatus: profileStatus,
      viewerData: metadata.viewerData,
      officerData: metadata.officerData,
    );
  }
  
  /// Create a copy of this model with modified attributes
  ///
  /// This method allows for creating a new instance with selectively updated fields.
  /// For fields not explicitly provided, the original values are retained.
  ///
  /// Setting 'isOfficer' will keep consistency with officer/viewer data.
  UserMetadataModel copyWith({
    bool? isOfficer,
    String? userId,
    String? roleId,
    String? profileStatus,
    String? email,
    OfficerModel? officerData,
    UserModel? viewerData,
    Map<String, dynamic>? additionalData,
  }) {
    final newIsOfficer = isOfficer ?? this.isOfficer;
    
    return UserMetadataModel(
      isOfficer: newIsOfficer,
      userId: userId ?? this.userId,
      roleId: roleId ?? this.roleId,
      profileStatus: profileStatus ?? this.profileStatus,
      email: email ?? this.email,
      // Only include officer data if the model is for an officer
      officerData: newIsOfficer ? (officerData ?? this.officerData) : null,
      // Only include viewer data if the model is not for an officer
      viewerData: !newIsOfficer ? (viewerData ?? this.viewerData) : null,
      additionalData: additionalData ?? this.additionalData,
    );
  }
  
  /// Create a new UserMetadataModel with profile status set to 'completed'
  UserMetadataModel markAsCompleted() {
    return copyWith(profileStatus: 'completed');
  }

  /// Create a new UserMetadataModel with updated officer data
  UserMetadataModel withUpdatedOfficerData(OfficerModel officer) {
    return copyWith(
      isOfficer: true,
      officerData: officer,
      profileStatus: 'completed',
    );
  }

  /// Create a new UserMetadataModel with updated viewer data
  UserMetadataModel withUpdatedViewerData(UserModel viewer) {
    return copyWith(
      isOfficer: false,
      viewerData: viewer,
      profileStatus: 'completed',
    );
  }
  
  // MARK: - Computed properties (getters)
  
  /// Primary identifier (NRP for officers, NIK for users)
  String? get identifier => isOfficer ? officerData?.nrp?.toString() : nik;
  
  /// User's NIK (delegated to viewerData if available)
  String? get nik => viewerData?.profile?.nik;

  /// User's phone number (delegated to appropriate model based on user type)
  String? get phone => isOfficer ? officerData?.phone : viewerData?.phone;

  /// User's name (delegated to appropriate model or fallback to email)
  String? get name {
    if (isOfficer && officerData?.name?.isNotEmpty == true) {
      return officerData!.name;
    }
    if (!isOfficer && viewerData?.profile?.fullName?.isNotEmpty == true) {
      return viewerData!.profile?.fullName;
    }
    return email?.split('@').first; // Fallback to email username
  }
  
  /// User's address (only available for regular users)
  Map<String, dynamic>? get address =>
      !isOfficer ? viewerData?.profile?.address : null;

  /// User's rank (only available for officers)
  String? get rank => isOfficer ? officerData?.rank : null;

  /// User's unit ID (only available for officers)
  String? get unitId => isOfficer ? officerData?.unitId : null;

  /// Get display name with fallback priority
  String? get displayName => name;

  /// Validate required fields based on user type
  List<String> validate() {
    final errors = <String>[];

    if (isOfficer) {
      if (officerData?.nrp == null) {
        errors.add('NRP is required for officers');
      }
      if (officerData?.unitId?.isEmpty != false) {
        errors.add('Unit ID is required for officers');
      }
    } else {
      if (nik?.isEmpty != false) {
        errors.add('NIK is required for regular users');
      }
    }

    // Common validations
    if (email?.isEmpty != false) {
      errors.add('Email is required');
    } else if (!_isValidEmail(email!)) {
      errors.add('Invalid email format');
    }

    return errors;
  }

  bool _isValidEmail(String email) {
    return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email);
  }

  /// Check if user data is complete
  bool get isComplete {
    return validate().isEmpty;
  }

  @override
  String toString() {
    return 'UserMetadataModel('
        'isOfficer: $isOfficer, '
        'identifier: $identifier, '
        'displayName: $displayName'
        ')';
  }
  
  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;

    return other is UserMetadataModel &&
        other.isOfficer == isOfficer &&
        other.identifier == identifier &&
        other.email == email;
  }
  
  @override
  int get hashCode {
    return isOfficer.hashCode ^ identifier.hashCode ^ email.hashCode;
  }
}
