import 'profile.dart';
import 'beacon.dart';

enum PostStatus {
  active('active'),
  flagged('flagged'),
  removed('removed');

  final String value;
  const PostStatus(this.value);

  static PostStatus fromString(String value) {
    return PostStatus.values.firstWhere(
      (status) => status.value == value,
      orElse: () => PostStatus.active,
    );
  }
}

enum ToneLabel {
  positive('positive'),
  neutral('neutral'),
  mixed('mixed'),
  negative('negative'),
  hostile('hostile');

  final String value;
  const ToneLabel(this.value);

  static ToneLabel fromString(String value) {
    return ToneLabel.values.firstWhere(
      (label) => label.value == value,
      orElse: () => ToneLabel.neutral,
    );
  }
}

class Post {
  final String id;
  final String authorId;
  final String? categoryId;
  final String body;
  final PostStatus status;
  final ToneLabel detectedTone;
  final double contentIntegrityScore;
  final DateTime createdAt;
  final DateTime? editedAt;
  final DateTime? expiresAt;
  final bool isEdited;
  final bool allowChain;
  final String? chainParentId;
  final PostPreview? chainParent;
  final String visibility;
  final DateTime? pinnedAt;

  final Profile? author;
  final int? likeCount;
  final int? saveCount;
  final int? commentCount;
  final int? viewCount;

  final bool? isLiked;
  final bool? isSaved;
  final String? imageUrl;
  final String? videoUrl;
  final String? thumbnailUrl;
  final int? durationMs;
  final bool? hasVideoContent;
  final String? bodyFormat;
  final String? backgroundId;
  final List<String>? tags;
  final Map<String, int>? reactions;
  final List<String>? myReactions;
  final Map<String, List<String>>? reactionUsers;
  
  final bool? isBeacon;
  final BeaconType? beaconType;
  final double? confidenceScore;
  final bool? isActiveBeacon;
  final String? beaconStatusColor;
  final String? severity;
  final String? incidentStatus;
  final int? radius;
  final int? verificationCount;
  final int? vouchCount;
  final int? reportCount;

  final double? latitude;
  final double? longitude;
  final double? distanceMeters;

  // Group / Neighborhood context
  final String? groupId;
  final String? groupName;

  final bool isSponsored;
  final String? advertiserName;
  final String? ctaLink;
  final String? ctaText;

  final bool isNsfw;
  final String? nsfwReason;

  // Link preview (OG metadata)
  final String? linkPreviewUrl;
  final String? linkPreviewTitle;
  final String? linkPreviewDescription;
  final String? linkPreviewImageUrl;
  final String? linkPreviewSiteName;

  bool get hasLinkPreview => linkPreviewUrl != null && linkPreviewUrl!.isNotEmpty;

  Post({
    required this.id,
    required this.authorId,
    this.categoryId,
    required this.body,
    required this.status,
    required this.detectedTone,
    required this.contentIntegrityScore,
    required this.createdAt,
    this.editedAt,
    this.expiresAt,
    this.isEdited = false,
    this.allowChain = true,
    this.chainParentId,
    this.chainParent,
    this.visibility = 'public',
    this.pinnedAt,
    this.author,
    this.likeCount,
    this.saveCount,
    this.commentCount,
    this.viewCount,
    this.isLiked,
    this.isSaved,
    this.imageUrl,
    this.videoUrl,
    this.thumbnailUrl,
    this.durationMs,
    this.hasVideoContent,
    this.bodyFormat,
    this.backgroundId,
    this.tags,
    this.reactions,
    this.myReactions,
    this.reactionUsers,
    this.isBeacon = false,
    this.beaconType,
    this.confidenceScore,
    this.isActiveBeacon,
    this.beaconStatusColor,
    this.severity,
    this.incidentStatus,
    this.radius,
    this.verificationCount,
    this.vouchCount,
    this.reportCount,
    this.latitude,
    this.longitude,
    this.distanceMeters,
    this.groupId,
    this.groupName,
    this.isSponsored = false,
    this.advertiserName,
    this.ctaLink,
    this.ctaText,
    this.isNsfw = false,
    this.nsfwReason,
    this.linkPreviewUrl,
    this.linkPreviewTitle,
    this.linkPreviewDescription,
    this.linkPreviewImageUrl,
    this.linkPreviewSiteName,
  });

  static int? _parseInt(dynamic value) {
    if (value is int) return value;
    if (value is num) return value.toInt();
    return null;
  }

  static double _parseDouble(dynamic value, {double fallback = 0.0}) {
    if (value is double) return value;
    if (value is int) return value.toDouble();
    if (value is num) return value.toDouble();
    return fallback;
  }

  static List<String>? _parseTags(dynamic value) {
    if (value == null) return null;
    if (value is List<dynamic>) {
      return value.map((e) => e.toString()).toList();
    }
    return null;
  }

  static Map<String, int>? _parseReactions(dynamic value) {
    if (value == null) return null;
    if (value is Map<String, dynamic>) {
      return value.map((key, val) => MapEntry(key, _parseInt(val) ?? 0));
    }
    return null;
  }

  static List<String>? _parseReactionsList(dynamic value) {
    if (value == null) return null;
    if (value is List) {
      return value.map((item) => item.toString()).toList();
    }
    return null;
  }

  static Map<String, List<String>>? _parseReactionUsers(dynamic value) {
    if (value == null) return null;
    if (value is Map<String, dynamic>) {
      return value.map((key, val) {
        if (val is List) {
          return MapEntry(key, val.map((item) => item.toString()).toList());
        }
        return MapEntry(key, <String>[]);
      });
    }
    return null;
  }

  static double _defaultCis(String tone) {
    switch (tone) {
      case 'positive':
        return 0.9;
      case 'neutral':
        return 0.8;
      case 'mixed':
        return 0.7;
      case 'negative':
        return 0.5;
      default:
        return 0.8;
    }
  }

  factory Post.fromJson(Map<String, dynamic> json) {
    final authorJson = json['author'] as Map<String, dynamic>?;
    final categoryJson = json['category'] as Map<String, dynamic>?;
    final metricsJson = json['metrics'] as Map<String, dynamic>?;
    final chainParentJson = json['chain_parent'] == null || json['chain_parent'] is! Map<String, dynamic>
        ? null
        : json['chain_parent'] as Map<String, dynamic>;
    final statusValue = json['status'] as String? ?? 'active';
    final toneValue =
        json['detected_tone'] as String? ?? json['tone_label'] as String? ?? 'neutral';
    final cisValue =
        json['content_integrity_score'] ?? json['cis_score'] ?? _defaultCis(toneValue);
    final editedAtValue = json['edited_at'] ?? json['updated_at'];

    return Post(
      id: json['id'] as String,
      authorId: json['author_id'] as String? ?? authorJson?['id'] as String? ?? '',
      categoryId: json['category_id'] as String? ?? categoryJson?['id'] as String?,
      body: json['body'] as String,
      status: PostStatus.fromString(statusValue),
      detectedTone: ToneLabel.fromString(toneValue),
      contentIntegrityScore: _parseDouble(cisValue, fallback: _defaultCis(toneValue)),
      createdAt: DateTime.parse(json['created_at'] as String),
      editedAt: editedAtValue != null
          ? DateTime.parse(editedAtValue as String)
          : null,
      expiresAt: json['expires_at'] != null
          ? DateTime.parse(json['expires_at'] as String)
          : null,
      isEdited: json['is_edited'] as bool? ?? false,
      allowChain: json['allow_chain'] as bool? ?? true,
      chainParentId: json['chain_parent_id'] as String?,
      chainParent:
          chainParentJson != null ? PostPreview.fromJson(chainParentJson) : null,
      visibility: json['visibility'] as String? ?? 'public',
      pinnedAt: json['pinned_at'] != null
          ? DateTime.parse(json['pinned_at'] as String)
          : null,
      author: authorJson != null ? Profile.fromJson(authorJson) : null,
      likeCount: _parseInt(metricsJson?['like_count'] ?? json['like_count']),
      saveCount: _parseInt(metricsJson?['save_count'] ?? json['save_count']),
      commentCount: _parseInt(json['comment_count']),
      viewCount: _parseInt(metricsJson?['view_count'] ?? json['view_count']),
      isLiked: json['is_liked'] as bool? ?? json['user_liked'] as bool?,
      isSaved: json['is_saved'] as bool? ?? json['user_saved'] as bool?,
      imageUrl: json['image_url'] as String?,
      videoUrl: json['video_url'] as String?,
      thumbnailUrl: json['thumbnail_url'] as String?,
      durationMs: _parseInt(json['duration_ms']),
      hasVideoContent: json['has_video_content'] as bool? ?? 
          ((json['video_url'] as String?)?.isNotEmpty == true || 
           (json['image_url'] as String?)?.toLowerCase().endsWith('.mp4') == true),
      bodyFormat: json['body_format'] as String?,

      backgroundId: json['background_id'] as String?,
      tags: _parseTags(json['tags']),
      reactions: _parseReactions(
          json['reactions'] ?? json['reaction_counts'] ?? json['reaction_map']),
      myReactions: _parseReactionsList(
          json['my_reactions'] ?? json['myReactions']),
      reactionUsers: _parseReactionUsers(
          json['reaction_users'] ?? json['reaction_users_preview']),
      isBeacon: json['is_beacon'] as bool?,
      beaconType: json['beacon_type'] != null ? BeaconType.fromString(json['beacon_type'] as String) : null,
      confidenceScore: _parseDouble(json['confidence_score']),
      isActiveBeacon: json['is_active_beacon'] as bool?,
      beaconStatusColor: json['status_color'] as String?,
      severity: json['severity'] as String?,
      incidentStatus: json['incident_status'] as String?,
      radius: _parseInt(json['radius']),
      verificationCount: _parseInt(json['verification_count']),
      vouchCount: _parseInt(json['vouch_count']),
      reportCount: _parseInt(json['report_count']),
      latitude: _parseLatitude(json),
      longitude: _parseLongitude(json),
      distanceMeters: _parseDouble(json['distance_meters']),
      groupId: json['group_id'] as String?,
      groupName: json['group_name'] as String?,
      isSponsored: json['is_sponsored'] as bool? ?? false,
      advertiserName: json['advertiser_name'] as String?,
      ctaLink: json['advertiser_cta_link'] as String?,
      ctaText: json['advertiser_cta_text'] as String?,
      isNsfw: json['is_nsfw'] as bool? ?? false,
      nsfwReason: json['nsfw_reason'] as String?,
      linkPreviewUrl: json['link_preview_url'] as String?,
      linkPreviewTitle: json['link_preview_title'] as String?,
      linkPreviewDescription: json['link_preview_description'] as String?,
      linkPreviewImageUrl: json['link_preview_image_url'] as String?,
      linkPreviewSiteName: json['link_preview_site_name'] as String?,
    );
  }

  static double? _parseLatitude(Map<String, dynamic> json) {
    final value = json['latitude'] ?? json['lat'] ?? json['beacon_lat'];
    if (value == null) return null;
    return _parseDouble(value);
  }

  static double? _parseLongitude(Map<String, dynamic> json) {
    final value = json['longitude'] ?? json['long'] ?? json['beacon_long'];
    if (value == null) return null;
    return _parseDouble(value);
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'author_id': authorId,
      'category_id': categoryId,
      'body': body,
      'status': status.value,
      'detected_tone': detectedTone.value,
      'content_integrity_score': contentIntegrityScore,
      'created_at': createdAt.toIso8601String(),
      'edited_at': editedAt?.toIso8601String(),
      'expires_at': expiresAt?.toIso8601String(),
      'allow_chain': allowChain,
      'chain_parent_id': chainParentId,
      'chain_parent': chainParent?.toJson(),
      'visibility': visibility,
      'pinned_at': pinnedAt?.toIso8601String(),
      'author': author?.toJson(),
      'like_count': likeCount,
      'save_count': saveCount,
      'comment_count': commentCount,
      'view_count': viewCount,
      'is_liked': isLiked,
      'is_saved': isSaved,
      'image_url': imageUrl,
      'video_url': videoUrl,
      'thumbnail_url': thumbnailUrl,
      'duration_ms': durationMs,
      'has_video_content': hasVideoContent,
      'tags': tags,
      'reactions': reactions,
      'my_reactions': myReactions,
      'reaction_users': reactionUsers,
      'is_nsfw': isNsfw,
      'nsfw_reason': nsfwReason,
      'link_preview_url': linkPreviewUrl,
      'link_preview_title': linkPreviewTitle,
      'link_preview_description': linkPreviewDescription,
      'link_preview_image_url': linkPreviewImageUrl,
      'link_preview_site_name': linkPreviewSiteName,
    };
  }
}

class PostPreview {
  final String id;
  final String body;
  final DateTime createdAt;
  final Profile? author;
  final Map<String, int>? reactions;
  final List<String>? myReactions;

  const PostPreview({
    required this.id,
    required this.body,
    required this.createdAt,
    this.author,
    this.reactions,
    this.myReactions,
  });

  factory PostPreview.fromJson(Map<String, dynamic> json) {
    final authorJson = json['author'] as Map<String, dynamic>?;
    return PostPreview(
      id: json['id'] as String,
      body: json['body'] as String? ?? '',
      createdAt: DateTime.parse(json['created_at'] as String),
      author: authorJson != null ? Profile.fromJson(authorJson) : null,
      reactions: Post._parseReactions(json['reactions'] ?? json['reaction_counts']),
      myReactions: Post._parseReactionsList(json['my_reactions'] ?? json['myReactions']),
    );
  }

  factory PostPreview.fromPost(Post post) {
    return PostPreview(
      id: post.id,
      body: post.body,
      createdAt: post.createdAt,
      author: post.author,
      reactions: post.reactions,
      myReactions: post.myReactions,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'body': body,
      'created_at': createdAt.toIso8601String(),
      'author': author?.toJson(),
      'reactions': reactions,
      'my_reactions': myReactions,
    };
  }
}

class ToneAnalysis {
  final ToneLabel tone;
  final double cis;
  final bool shouldReject;
  final String? rejectReason;
  final List<String> flags;

  ToneAnalysis({
    required this.tone,
    required this.cis,
    required this.shouldReject,
    this.rejectReason,
    required this.flags,
  });

  factory ToneAnalysis.fromJson(Map<String, dynamic> json) {
    return ToneAnalysis(
      tone: ToneLabel.fromString(json['tone'] as String),
      cis: (json['cis'] as num).toDouble(),
      shouldReject: json['should_reject'] as bool,
      rejectReason: json['reject_reason'] as String?,
      flags: (json['flags'] as List<dynamic>).cast<String>(),
    );
  }
}

extension PostBeaconExtension on Post {
  bool get isBeaconPost => isBeacon == true && beaconType != null;

  dynamic get beaconColor => beaconType?.color;

  dynamic get beaconIcon => beaconType?.icon;

  Beacon toBeacon() {
    if (!isBeaconPost) {
      throw Exception('Cannot convert non-beacon Post to Beacon');
    }

    final status = confidenceScore != null
        ? BeaconStatus.fromConfidence(confidenceScore!)
        : BeaconStatus.yellow;

    return Beacon(
      id: id,
      body: body,
      authorId: authorId,
      beaconType: beaconType!,
      confidenceScore: confidenceScore ?? 0.5,
      isActiveBeacon: isActiveBeacon ?? true,
      status: status,
      createdAt: createdAt,
      distanceMeters: distanceMeters ?? 0,
      imageUrl: imageUrl,
      beaconLat: latitude,
      beaconLong: longitude,
      authorHandle: author?.handle,
      authorDisplayName: author?.displayName,
      authorAvatarUrl: author?.avatarUrl,
      vouchCount: vouchCount,
      reportCount: reportCount,
      userVote: null,
      groupId: groupId,
      severity: BeaconSeverity.fromString(severity ?? 'medium'),
      incidentStatus: BeaconIncidentStatus.fromString(incidentStatus ?? 'active'),
      radius: radius ?? 500,
      verificationCount: verificationCount ?? 0,
    );
  }
}

/// FocusContext represents the minimal data needed for the Focus-Context view
class FocusContext {
  final Post targetPost;
  final Post? parentPost;
  final List<Post> children;
  final List<Post> parentChildren;

  const FocusContext({
    required this.targetPost,
    this.parentPost,
    required this.children,
    this.parentChildren = const [],
  });

  factory FocusContext.fromJson(Map<String, dynamic> json) {
    return FocusContext(
      targetPost: Post.fromJson(json['target_post']),
      parentPost: json['parent_post'] != null ? Post.fromJson(json['parent_post']) : null,
      children: (json['children'] as List?)
          ?.map((child) => Post.fromJson(child))
          .toList() ?? [],
      parentChildren: (json['parent_children'] as List?)
          ?.map((child) => Post.fromJson(child))
          .toList() ?? [],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'target_post': targetPost.toJson(),
      'parent_post': parentPost?.toJson(),
      'children': children.map((child) => child.toJson()).toList(),
      'parent_children': parentChildren.map((child) => child.toJson()).toList(),
    };
  }
}
