/// This file is generated by claude.ai
/// A lightweight alternative api for `package:jellyfin_dart`, since it adds ~1.5MB for the base apk

// ignore_for_file: unused_field

part of 'music_web_server_base.dart';

enum _JellyfinImageType {
  primary('Primary'),
  backdrop('Backdrop'),
  banner('Banner'),
  logo('Logo'),
  thumb('Thumb');

  const _JellyfinImageType(this.value);
  final String value;
}

enum _JellyfinItemKind {
  audio('Audio'),
  video('Video'),
  musicVideo('MusicVideo');

  const _JellyfinItemKind(this.value);
  final String value;
}

enum _JellyfinItemField {
  mediaStreams('MediaStreams'),
  mediaSources('MediaSources'),
  genres('Genres'),
  dateCreated('DateCreated'),
  dateModified('dateModified'),
  overview('Overview'),
  path('Path'),
  tags('Tags');

  const _JellyfinItemField(this.value);
  final String value;
}

enum _JellyfinVideoType {
  videoFile('VideoFile'),
  iso('Iso'),
  dvd('Dvd'),
  bluRay('BluRay');

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

  static _JellyfinVideoType? fromJson(String? value) => _JellyfinVideoType.values.where((e) => e.value == value).firstOrNull;
}

class _JellyfinUserItemData {
  final double? rating;
  final bool? isFavorite;
  final bool? played;
  final double? playedPercentage;
  final int? playbackPositionTicks;

  const _JellyfinUserItemData({
    this.rating,
    this.isFavorite,
    this.played,
    this.playedPercentage,
    this.playbackPositionTicks,
  });

  factory _JellyfinUserItemData.fromJson(Map<String, dynamic> j) => _JellyfinUserItemData(
    rating: (j['Rating'] as num?)?.toDouble(),
    isFavorite: j['IsFavorite'] as bool?,
    played: j['Played'] as bool?,
    playedPercentage: (j['PlayedPercentage'] as num?)?.toDouble(),
    playbackPositionTicks: j['PlaybackPositionTicks'] as int?,
  );
}

class _JellyfinMediaStream {
  final String? codec;
  final String? language;
  final String? type;
  final int? bitRate;
  final int? channels;
  final int? sampleRate;
  final int? width;
  final int? height;

  const _JellyfinMediaStream({
    this.codec,
    this.language,
    this.type,
    this.bitRate,
    this.channels,
    this.sampleRate,
    this.width,
    this.height,
  });

  factory _JellyfinMediaStream.fromJson(Map<String, dynamic> j) => _JellyfinMediaStream(
    codec: j['Codec'] as String?,
    language: j['Language'] as String?,
    type: j['Type'] as String?,
    bitRate: j['BitRate'] as int?,
    channels: j['Channels'] as int?,
    sampleRate: j['SampleRate'] as int?,
    width: j['Width'] as int?,
    height: j['Height'] as int?,
  );
}

class _JellyfinMediaSource {
  final String? id;
  final String? path;
  final String? container;
  final int? size;
  final int? bitrate;
  final List<_JellyfinMediaStream> mediaStreams;

  const _JellyfinMediaSource({
    this.id,
    this.path,
    this.container,
    this.size,
    this.bitrate,
    this.mediaStreams = const [],
  });

  factory _JellyfinMediaSource.fromJson(Map<String, dynamic> j) => _JellyfinMediaSource(
    id: j['Id'] as String?,
    path: j['Path'] as String?,
    container: j['Container'] as String?,
    size: j['Size'] as int?,
    bitrate: j['Bitrate'] as int?,
    mediaStreams: (j['MediaStreams'] as List<dynamic>?)?.map((e) => _JellyfinMediaStream.fromJson(e as Map<String, dynamic>)).toList() ?? [],
  );
}

class _JellyfinItem {
  final String? id;
  final String? name;
  final String? type;
  final String? overview;
  final String? path;
  final DateTime? dateCreated;
  final DateTime? dateModified;
  final List<String> genres;
  final List<String> tags;
  final List<_JellyfinMediaStream> mediaStreams;
  final List<_JellyfinMediaSource> mediaSources;
  // Music-specific
  final List<String> artists;
  final String? albumArtist;
  final String? album;
  final String? albumId;
  final int? indexNumber; // track number
  final int? parentIndexNumber; // disc number
  final int? productionYear;
  final int? runTimeTicks; // 100-nanosecond units

  final String? container;
  final _JellyfinUserItemData? userData;
  final _JellyfinVideoType? videoType;

  const _JellyfinItem({
    this.id,
    this.name,
    this.type,
    this.overview,
    this.path,
    this.dateCreated,
    this.dateModified,
    this.genres = const [],
    this.tags = const [],
    this.mediaStreams = const [],
    this.mediaSources = const [],
    this.artists = const [],
    this.albumArtist,
    this.album,
    this.albumId,
    this.indexNumber,
    this.parentIndexNumber,
    this.productionYear,
    this.runTimeTicks,
    this.container,
    this.userData,
    this.videoType,
  });

  factory _JellyfinItem.fromJson(Map<String, dynamic> j) => _JellyfinItem(
    id: j['Id'] as String?,
    name: j['Name'] as String?,
    type: j['Type'] as String?,
    overview: j['Overview'] as String?,
    path: j['Path'] as String?,
    dateCreated: j['DateCreated'] != null ? DateTime.tryParse(j['DateCreated'] as String) : null,
    dateModified: j['DateModified'] != null ? DateTime.tryParse(j['DateModified'] as String) : null,
    genres: (j['Genres'] as List<dynamic>?)?.cast<String>() ?? [],
    tags: (j['Tags'] as List<dynamic>?)?.cast<String>() ?? [],
    mediaStreams: (j['MediaStreams'] as List<dynamic>?)?.map((e) => _JellyfinMediaStream.fromJson(e as Map<String, dynamic>)).toList() ?? [],
    mediaSources: (j['MediaSources'] as List<dynamic>?)?.map((e) => _JellyfinMediaSource.fromJson(e as Map<String, dynamic>)).toList() ?? [],
    artists: (j['Artists'] as List<dynamic>?)?.cast<String>() ?? [],
    albumArtist: j['AlbumArtist'] as String?,
    album: j['Album'] as String?,
    albumId: j['AlbumId'] as String?,
    indexNumber: j['IndexNumber'] as int?,
    parentIndexNumber: j['ParentIndexNumber'] as int?,
    productionYear: j['ProductionYear'] as int?,
    runTimeTicks: j['RunTimeTicks'] as int?,
    container: j['Container'] as String?,
    userData: j['UserData'] != null ? _JellyfinUserItemData.fromJson(j['UserData'] as Map<String, dynamic>) : null,
    videoType: _JellyfinVideoType.fromJson(j['VideoType'] as String?),
  );
}

class _JellyfinApi {
  final Dio dio;

  _JellyfinApi(String baseUrl)
    : dio = Dio(
        BaseOptions(
          baseUrl: baseUrl,
          connectTimeout: const Duration(seconds: 10),
          receiveTimeout: const Duration(seconds: 30),
        ),
      );

  void setAuthHeader({
    required String deviceId,
    required String version,
    String? token,
  }) {
    final buf = StringBuffer()
      ..write('MediaBrowser Client="Namida"')
      ..write(', Device="Namida"')
      ..write(', DeviceId="$deviceId"')
      ..write(', Version="$version"');
    if (token != null) buf.write(', Token="$token"');
    dio.options.headers['X-Emby-Authorization'] = buf.toString();
  }

  Future<({String accessToken, String userId})?> authenticate({
    required String username,
    required String password,
  }) async {
    final res = await dio.post<Map<String, dynamic>>(
      '/Users/AuthenticateByName',
      data: {'Username': username, 'Pw': password},
    );
    final data = res.data;
    if (data == null) return null;
    final token = data['AccessToken'] as String?;
    final userId = (data['User'] as Map<String, dynamic>?)?['Id'] as String?;
    if (token == null || userId == null) return null;
    return (accessToken: token, userId: userId);
  }

  Future<void> getSystemInfo() => dio.get<void>('/System/Info');

  Future<Uint8List?> getItemImage({
    required String itemId,
    required _JellyfinImageType imageType,
  }) async {
    try {
      final res = await dio.get<Uint8List>(
        '/Items/$itemId/Images/${imageType.value}',
        options: Options(responseType: ResponseType.bytes),
      );
      return res.data;
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        // -- get parent image instead if item image was not found
        final itemRes = await dio.get('/Items/$itemId');
        final albumId = itemRes.data['AlbumId'];
        final res = await dio.get<Uint8List>(
          '/Items/$albumId/Images/Primary',
          queryParameters: {
            'imageIndex': 0,
          },
          options: Options(responseType: ResponseType.bytes),
        );
        return res.data;
      } else {
        rethrow;
      }
    }
  }
}
