/// Nextcloud share types (subset the app uses).
const int kShareTypeUser = 0;
const int kShareTypeGroup = 1;
const int kShareTypePublicLink = 3;
const int kShareTypeEmail = 4;

/// A Talk conversation ("room") the file is linked into.
const int kShareTypeRoom = 10;

/// A single share as returned by the OCS `files_sharing` API.
///
/// Parsed by hand rather than via code generation: OCS is inconsistent about
/// types across versions (e.g. `id`/`share_type` may arrive as either strings
/// or numbers) and omits optional fields entirely, so defensive parsing is
/// safer than generated `as int` casts.
class Share {
  final int id;
  final int shareType;
  final int permissions;

  /// Server path of the shared item (e.g. `/Documents/x.pdf`).
  final String? path;

  /// `'file'` or `'folder'`.
  final String? itemType;

  /// Recipient id (user/group id); empty for public links.
  final String? shareWith;
  final String? shareWithDisplayname;

  /// Public link URL (only set for [kShareTypePublicLink]).
  final String? url;

  /// Raw expiration as `"YYYY-MM-DD HH:MM:SS"` (or null when none).
  final String? expiration;
  final String? label;

  /// Redacted password marker: the server returns `null` when no password is
  /// set and a placeholder (`"redacted"`) when one is — never the real value.
  final String? password;

  const Share({
    required this.id,
    required this.shareType,
    required this.permissions,
    this.path,
    this.itemType,
    this.shareWith,
    this.shareWithDisplayname,
    this.url,
    this.expiration,
    this.label,
    this.password,
  });

  bool get isPublicLink => shareType == kShareTypePublicLink;
  bool get isGroup => shareType == kShareTypeGroup;
  bool get isEmail => shareType == kShareTypeEmail;
  bool get isRoom => shareType == kShareTypeRoom;
  bool get isFolder => itemType == 'folder';

  /// Whether a (link) password is currently set. See [password].
  bool get hasPassword => password != null && password!.isNotEmpty;

  /// Best display title for the share row.
  String get displayTitle {
    if (isPublicLink) return label?.isNotEmpty == true ? label! : 'Link';
    final name = shareWithDisplayname;
    if (name != null && name.isNotEmpty) return name;
    return shareWith ?? 'Unbekannt';
  }

  /// Human label for the kind of share (for subtitles/headers).
  String get kindLabel {
    if (isPublicLink) return 'Öffentlicher Link';
    if (isGroup) return 'Gruppe';
    if (isRoom) return 'Talk-Chat';
    if (isEmail) return 'E-Mail';
    if (shareType == kShareTypeUser) return 'Person';
    return 'Freigabe';
  }

  static int _asInt(Object? value, {int fallback = 0}) {
    if (value is int) return value;
    if (value is String) return int.tryParse(value) ?? fallback;
    return fallback;
  }

  static String? _asString(Object? value) {
    if (value == null) return null;
    final s = value.toString();
    return s.isEmpty ? null : s;
  }

  factory Share.fromJson(Map<String, dynamic> json) => Share(
    id: _asInt(json['id']),
    shareType: _asInt(json['share_type']),
    permissions: _asInt(json['permissions']),
    path: _asString(json['path']),
    itemType: _asString(json['item_type']),
    shareWith: _asString(json['share_with']),
    shareWithDisplayname: _asString(json['share_with_displayname']),
    url: _asString(json['url']),
    expiration: _asString(json['expiration']),
    label: _asString(json['label']),
    password: _asString(json['password']),
  );
}
