import 'dart:convert';
import 'package:plezy/media/ids.dart';

import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:plezy/database/app_database.dart';
import 'package:plezy/services/plex_api_cache.dart';
import 'package:plezy/services/plex_client.dart';

import '../test_helpers/backend_client_fixtures.dart';

http.Response _json(Object body) => http.Response(jsonEncode(body), 200, headers: {'content-type': 'application/json'});

void main() {
  late AppDatabase db;

  setUp(() {
    db = AppDatabase.forTesting(NativeDatabase.memory());
    PlexApiCache.initialize(db);
  });

  tearDown(() async {
    await db.close();
  });

  PlexClient makeClient(Future<http.Response> Function(http.Request request) handler) =>
      testPlexClient(serverId: ServerId('plex-1'), serverName: 'Plex', handler: handler);

  test('search defaults to 100 movie, TV, and music candidates', () async {
    final captured = <Uri>[];
    final client = makeClient((request) async {
      captured.add(request.url);
      if (request.url.path == '/library/search') {
        return _json({
          'MediaContainer': {
            'SearchResult': [
              {
                'score': 90,
                'Metadata': {'ratingKey': 'movie-1', 'type': 'movie', 'title': 'The Movie'},
              },
            ],
          },
        });
      }
      return http.Response('unexpected request', 500);
    });
    addTearDown(client.close);

    final results = await client.searchItems('the');

    expect(results.map((item) => item.id), ['movie-1']);
    expect(captured, hasLength(1));
    expect(captured.single.path, '/library/search');
    expect(captured.single.queryParameters['limit'], '100');
    expect(captured.single.queryParameters['X-Plex-Container-Size'], '100');
    expect(captured.single.queryParameters['searchTypes'], 'movies,tv,music');
  });

  test('saturated mixed search supplements omitted media categories and deduplicates results', () async {
    final captured = <Uri>[];
    final primaryResults = <Map<String, Object>>[
      for (var index = 0; index < 99; index++)
        {
          'score': 90,
          'Metadata': {'ratingKey': 'movie-$index', 'type': 'movie', 'title': 'Target Movie $index'},
        },
      {
        'score': 10,
        'Metadata': {'ratingKey': 'collection-1', 'type': 'collection', 'title': 'Target Collection'},
      },
    ];
    final client = makeClient((request) async {
      captured.add(request.url);
      final searchTypes = request.url.queryParameters['searchTypes'];
      final searchResults = switch (searchTypes) {
        'movies,tv,music' => primaryResults,
        'tv' => [
          {
            'score': 100,
            'Metadata': {'ratingKey': 'show-1', 'type': 'show', 'title': 'Target'},
          },
          {
            'score': 90,
            'Metadata': {'ratingKey': 'movie-0', 'type': 'movie', 'title': 'Target Movie 0'},
          },
        ],
        'music' => [
          {
            'score': 100,
            'Metadata': {'ratingKey': 'artist-1', 'type': 'artist', 'title': 'Target'},
          },
        ],
        _ => <Map<String, Object>>[],
      };
      return _json({
        'MediaContainer': {'SearchResult': searchResults},
      });
    });
    addTearDown(client.close);

    final results = await client.searchItems('Target');
    final ids = results.map((item) => item.id).toList();

    expect(captured.map((uri) => uri.queryParameters['searchTypes']).toSet(), {'movies,tv,music', 'tv', 'music'});
    expect(ids.where((id) => id == 'movie-0'), hasLength(1));
    expect(ids, containsAll(['show-1', 'artist-1']));
    expect(ids, hasLength(101));
  });

  test('supplemental category failure keeps saturated primary results', () async {
    final capturedSearchTypes = <String?>[];
    final primaryResults = <Map<String, Object>>[
      for (var index = 0; index < 99; index++)
        {
          'score': 90,
          'Metadata': {'ratingKey': 'movie-$index', 'type': 'movie', 'title': 'Target Movie $index'},
        },
      {
        'score': 80,
        'Metadata': {'ratingKey': 'artist-1', 'type': 'artist', 'title': 'Target Artist'},
      },
    ];
    final client = makeClient((request) async {
      final searchTypes = request.url.queryParameters['searchTypes'];
      capturedSearchTypes.add(searchTypes);
      if (searchTypes == 'movies,tv,music') {
        return _json({
          'MediaContainer': {'SearchResult': primaryResults},
        });
      }
      return http.Response('temporary failure', 500);
    });
    addTearDown(client.close);

    final results = await client.searchItems('Target');

    expect(capturedSearchTypes, ['movies,tv,music', 'tv']);
    expect(results, hasLength(100));
    expect(results.map((item) => item.id), containsAll(['movie-0', 'artist-1']));
  });
}
