import 'package:freezed_annotation/freezed_annotation.dart';

part 'script_widget.freezed.dart';
part 'script_widget.g.dart';

@freezed
abstract class ScriptWidgetOption with _$ScriptWidgetOption {
  const factory ScriptWidgetOption({
    @Default('') String title,
    @JsonKey(fromJson: _stringFromAny) @Default('') String value,
  }) = _ScriptWidgetOption;

  factory ScriptWidgetOption.fromJson(Map<String, dynamic> json) =>
      _$ScriptWidgetOptionFromJson(json);
}

@freezed
abstract class ScriptWidgetBelongTo with _$ScriptWidgetBelongTo {
  const factory ScriptWidgetBelongTo({
    @Default('') String paramName,
    @JsonKey(fromJson: _stringListFromAny)
    @Default(<String>[])
    List<String> value,
  }) = _ScriptWidgetBelongTo;

  factory ScriptWidgetBelongTo.fromJson(Map<String, dynamic> json) =>
      _$ScriptWidgetBelongToFromJson(json);
}

@freezed
abstract class ScriptWidgetParam with _$ScriptWidgetParam {
  const factory ScriptWidgetParam({
    required String name,
    @Default('') String title,
    @Default('input') String type,
    @Default('') String description,
    Object? value,
    ScriptWidgetBelongTo? belongTo,
    @Default(<ScriptWidgetOption>[]) List<ScriptWidgetOption> placeholders,
    @Default(<ScriptWidgetOption>[]) List<ScriptWidgetOption> enumOptions,
  }) = _ScriptWidgetParam;

  factory ScriptWidgetParam.fromJson(Map<String, dynamic> json) =>
      _$ScriptWidgetParamFromJson(json);
}

Map<String, Object?> buildScriptWidgetParameterDefaults(
  List<ScriptWidgetParam> parameters,
) {
  return <String, Object?>{
    for (final parameter in parameters)
      parameter.name: _resolveScriptWidgetParameterDefault(parameter),
  };
}

Object? _resolveScriptWidgetParameterDefault(ScriptWidgetParam parameter) {
  if (parameter.value != null) return parameter.value;
  if (parameter.enumOptions.isNotEmpty) {
    return parameter.enumOptions.first.value;
  }
  if (parameter.placeholders.isNotEmpty) {
    return parameter.placeholders.first.value;
  }
  return '';
}

@freezed
abstract class ScriptWidgetModule with _$ScriptWidgetModule {
  const factory ScriptWidgetModule({
    @Default('') String id,
    required String title,
    @Default('') String description,
    @Default(false) bool requiresWebView,
    required String functionName,
    @Default(false) bool sectionMode,
    @Default(3600) int cacheDuration,
    @Default('list') String type,
    @Default(<ScriptWidgetParam>[]) List<ScriptWidgetParam> params,
  }) = _ScriptWidgetModule;

  factory ScriptWidgetModule.fromJson(Map<String, dynamic> json) =>
      _$ScriptWidgetModuleFromJson(json);
}

@freezed
abstract class ScriptWidgetManifest with _$ScriptWidgetManifest {
  const factory ScriptWidgetManifest({
    required String id,
    required String title,
    @Default('') String description,
    @Default('') String author,
    @Default('') String site,
    @Default('0.0.0') String version,
    @Default('0.0.1') String requiredVersion,
    @Default(60) int detailCacheDuration,
    @Default(<ScriptWidgetParam>[]) List<ScriptWidgetParam> globalParams,
    @Default(<ScriptWidgetModule>[]) List<ScriptWidgetModule> modules,
    ScriptWidgetModule? search,
  }) = _ScriptWidgetManifest;

  factory ScriptWidgetManifest.fromJson(Map<String, dynamic> json) =>
      _$ScriptWidgetManifestFromJson(json);
}

@freezed
abstract class ScriptVideoPerson with _$ScriptVideoPerson {
  const factory ScriptVideoPerson({
    @JsonKey(fromJson: _stringFromAny) @Default('') String id,
    @Default('') String title,
    @Default('') String avatar,
    @Default('') String role,
  }) = _ScriptVideoPerson;

  factory ScriptVideoPerson.fromJson(Map<String, dynamic> json) =>
      _$ScriptVideoPersonFromJson(json);
}

@freezed
abstract class ScriptVideoGenre with _$ScriptVideoGenre {
  const factory ScriptVideoGenre({
    @JsonKey(fromJson: _stringFromAny) @Default('') String id,
    @Default('') String title,
  }) = _ScriptVideoGenre;

  factory ScriptVideoGenre.fromJson(Map<String, dynamic> json) =>
      _$ScriptVideoGenreFromJson(json);
}

@freezed
abstract class ScriptVideoTrailer with _$ScriptVideoTrailer {
  const factory ScriptVideoTrailer({
    @Default('') String coverUrl,
    @Default('') String url,
  }) = _ScriptVideoTrailer;

  factory ScriptVideoTrailer.fromJson(Map<String, dynamic> json) =>
      _$ScriptVideoTrailerFromJson(json);
}

@freezed
abstract class ScriptVideoItem with _$ScriptVideoItem {
  const factory ScriptVideoItem({
    @JsonKey(fromJson: _stringFromAny) required String id,
    @Default('link') String type,
    @Default('') String title,
    @Default('') String coverUrl,
    @JsonKey(readValue: _readPosterPath) @Default('') String posterPath,
    @Default('') String detailPoster,
    @Default('') String backdropPath,
    @Default(<String>[]) List<String> backdropPaths,
    @Default('') String releaseDate,
    @Default('') String mediaType,
    @JsonKey(fromJson: _doubleFromAny) double? rating,
    @Default('') String genreTitle,
    @Default(<ScriptVideoGenre>[]) List<ScriptVideoGenre> genreItems,
    @Default(<ScriptVideoPerson>[]) List<ScriptVideoPerson> peoples,
    @JsonKey(fromJson: _intFromAny) int? duration,
    @Default('') String durationText,
    @Default('') String previewUrl,
    @Default(<ScriptVideoTrailer>[]) List<ScriptVideoTrailer> trailers,
    @Default('') String videoUrl,
    @Default('') String link,
    @JsonKey(fromJson: _intFromAny) int? episode,
    @Default('') String description,
    @Default('app') String playerType,
    @Default(<ScriptVideoItem>[]) List<ScriptVideoItem> childItems,
    @Default(<ScriptVideoItem>[]) List<ScriptVideoItem> episodeItems,
    @Default(<ScriptVideoItem>[]) List<ScriptVideoItem> relatedItems,
  }) = _ScriptVideoItem;

  factory ScriptVideoItem.fromJson(Map<String, dynamic> json) =>
      _$ScriptVideoItemFromJson(json);
}

@freezed
abstract class ScriptVideoResource with _$ScriptVideoResource {
  const factory ScriptVideoResource({
    @Default('') String name,
    @Default('') String description,
    required String url,
    @JsonKey(readValue: _readResourceHeaders)
    @Default(<String, String>{})
    Map<String, String> customHeaders,
    @Default('app') String playerType,
  }) = _ScriptVideoResource;

  factory ScriptVideoResource.fromJson(Map<String, dynamic> json) =>
      _$ScriptVideoResourceFromJson(json);
}

@freezed
abstract class InstalledScriptWidget with _$InstalledScriptWidget {
  const factory InstalledScriptWidget({
    required ScriptWidgetManifest manifest,
    required String scriptSource,
    @Default('') String sourceUrl,
    @Default(true) bool enabled,
    @Default(<String, Object?>{}) Map<String, Object?> globalParams,
    required DateTime installedAt,
    required DateTime updatedAt,
  }) = _InstalledScriptWidget;

  factory InstalledScriptWidget.fromJson(Map<String, dynamic> json) =>
      _$InstalledScriptWidgetFromJson(json);
}

@freezed
abstract class ScriptWidgetCatalogEntry with _$ScriptWidgetCatalogEntry {
  const factory ScriptWidgetCatalogEntry({
    required String id,
    required String title,
    @Default('') String description,
    @Default('') String requiredVersion,
    @Default('') String version,
    @Default('') String author,
    required String url,
  }) = _ScriptWidgetCatalogEntry;

  factory ScriptWidgetCatalogEntry.fromJson(Map<String, dynamic> json) =>
      _$ScriptWidgetCatalogEntryFromJson(json);
}

@freezed
abstract class ScriptWidgetCatalog with _$ScriptWidgetCatalog {
  const factory ScriptWidgetCatalog({
    required String title,
    @Default('') String description,
    @Default('') String icon,
    @Default(<ScriptWidgetCatalogEntry>[])
    List<ScriptWidgetCatalogEntry> widgets,
  }) = _ScriptWidgetCatalog;

  factory ScriptWidgetCatalog.fromJson(Map<String, dynamic> json) =>
      _$ScriptWidgetCatalogFromJson(json);
}

@freezed
abstract class ScriptWidgetSubscription with _$ScriptWidgetSubscription {
  const factory ScriptWidgetSubscription({
    required String url,
    required ScriptWidgetCatalog catalog,
    required DateTime refreshedAt,
  }) = _ScriptWidgetSubscription;

  factory ScriptWidgetSubscription.fromJson(Map<String, dynamic> json) =>
      _$ScriptWidgetSubscriptionFromJson(json);
}

String _stringFromAny(Object? value) => value?.toString() ?? '';

int? _intFromAny(Object? value) {
  if (value is num) return value.toInt();
  return int.tryParse(value?.toString() ?? '');
}

double? _doubleFromAny(Object? value) {
  if (value is num) return value.toDouble();
  return double.tryParse(value?.toString() ?? '');
}

List<String> _stringListFromAny(Object? value) {
  if (value is List) {
    return value.map((item) => item.toString()).toList(growable: false);
  }
  if (value == null) return const <String>[];
  return <String>[value.toString()];
}

Object? _readPosterPath(Map<dynamic, dynamic> json, String key) {
  return json[key] ?? json['posterUrl'] ?? json['poster_url'];
}

Object? _readResourceHeaders(Map<dynamic, dynamic> json, String key) {
  final value = json[key] ?? json['headers'];
  if (value is! Map) return const <String, String>{};
  return <String, String>{
    for (final entry in value.entries)
      entry.key.toString(): entry.value.toString(),
  };
}
