import 'dart:convert';
import 'dart:io';

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';

import 'package:appsok/src/models/jenkins_build.dart';
import 'package:appsok/src/services/artifact_staging_service.dart';
import 'package:appsok/src/services/jenkins_artifact_session.dart';
import 'package:appsok/src/services/jenkins_client.dart';
import 'package:appsok/src/services/token_store.dart';

void main() {
  const credentials = JenkinsCredentials(username: 'user', apiToken: 'token');
  const baseUrl = 'https://jenkins.example';

  setUp(() {
    FlutterSecureStorage.setMockInitialValues({});
  });

  test('restore validates stored session with current user api', () async {
    FlutterSecureStorage.setMockInitialValues({
      'jenkins.baseUrl': baseUrl,
      'jenkins.username': credentials.username,
      'jenkins.apiToken': credentials.apiToken,
    });

    Uri? capturedUri;
    Map<String, String>? capturedHeaders;

    final session = JenkinsArtifactSession(
      store: TokenStore(),
      client: JenkinsClient(
        client: MockClient((request) async {
          capturedUri = request.url;
          capturedHeaders = request.headers;

          if (request.url.path.endsWith('whoAmI/api/json')) {
            return http.Response(jsonEncode({'id': 'user'}), 200);
          }

          return http.Response('unexpected', 400);
        }),
      ),
      stager: ArtifactStagingService(),
    );

    final restored = await session.restore();

    expect(restored, isTrue);
    expect(capturedUri?.path, '/whoAmI/api/json');
    expect(
      capturedHeaders?['Authorization'],
      'Basic ${base64Encode(utf8.encode('user:token'))}',
    );
  });

  test(
    'loadJobs delegates to JenkinsClient with stored base url and credentials',
    () async {
      FlutterSecureStorage.setMockInitialValues({
        'jenkins.baseUrl': baseUrl,
        'jenkins.username': credentials.username,
        'jenkins.apiToken': credentials.apiToken,
      });

      Uri? jobsUri;

      final session = JenkinsArtifactSession(
        store: TokenStore(),
        client: JenkinsClient(
          client: MockClient((request) async {
            if (request.url.path.endsWith('whoAmI/api/json')) {
              return http.Response(jsonEncode({'id': 'user'}), 200);
            }

            if (request.url.path.endsWith('api/json')) {
              jobsUri = request.url;
              return http.Response(jsonEncode({'jobs': []}), 200);
            }

            return http.Response('unexpected', 400);
          }),
        ),
        stager: ArtifactStagingService(),
      );

      await session.restore();
      await session.loadJobs();

      expect(jobsUri, isNotNull);
      expect(jobsUri?.path, '/api/json');
      expect(jobsUri?.host, 'jenkins.example');
    },
  );

  test(
    'downloadArtifact builds Jenkins artifact URL from build url and relative path',
    () async {
      FlutterSecureStorage.setMockInitialValues({
        'jenkins.baseUrl': baseUrl,
        'jenkins.username': credentials.username,
        'jenkins.apiToken': credentials.apiToken,
      });

      Uri? artifactUri;

      final session = JenkinsArtifactSession(
        store: TokenStore(),
        client: JenkinsClient(
          client: MockClient((request) async {
            if (request.url.path.endsWith('whoAmI/api/json')) {
              return http.Response(jsonEncode({'id': 'user'}), 200);
            }

            if (request.url.path.endsWith('artifact/file.apk')) {
              artifactUri = request.url;
              return http.Response.bytes([], 200);
            }

            if (request.url.path.endsWith('api/json')) {
              return http.Response(jsonEncode({'jobs': []}), 200);
            }

            return http.Response('unexpected', 400);
          }),
        ),
        stager: ArtifactStagingService(),
      );

      await session.restore();

      final task = session.downloadArtifact(
        JenkinsBuild(
          number: 1,
          jobName: 'app',
          url: Uri.parse('$baseUrl/job/app/1/'),
          startedAt: DateTime(2026),
          result: 'SUCCESS',
          artifacts: const [
            BuildArtifact(
              fileName: 'app.apk',
              relativePath: 'artifact/file.apk',
            ),
          ],
        ),
        const BuildArtifact(
          fileName: 'file.apk',
          relativePath: 'artifact/file.apk',
        ),
      );

      await task.events.toList();

      expect(artifactUri, isNotNull);
      expect(artifactUri?.path, '/job/app/1/artifact/artifact/file.apk');
    },
  );

  test('returns unavailable state when session config is missing', () async {
    final session = JenkinsArtifactSession(
      store: TokenStore(),
      client: JenkinsClient(
        client: MockClient((_) async => http.Response('', 200)),
      ),
      stager: ArtifactStagingService(),
    );

    final restored = await session.restore();

    expect(restored, isFalse);
    expect(session.isRestored, isFalse);
  });

  test('clear removes stored session and in-memory restored state', () async {
    FlutterSecureStorage.setMockInitialValues({
      'jenkins.baseUrl': baseUrl,
      'jenkins.username': credentials.username,
      'jenkins.apiToken': credentials.apiToken,
    });

    final store = TokenStore();
    final session = JenkinsArtifactSession(
      store: store,
      client: JenkinsClient(
        client: MockClient((request) async {
          if (request.url.path.endsWith('whoAmI/api/json')) {
            return http.Response(jsonEncode({'id': 'user'}), 200);
          }

          return http.Response('unexpected', 400);
        }),
      ),
      stager: ArtifactStagingService(),
    );

    expect(await session.restore(), isTrue);
    expect(session.isRestored, isTrue);

    await session.clear();

    expect(session.isRestored, isFalse);
    expect(await store.readSession(), isNull);
  });

  test('restoreDetailed returns missing when config is missing', () async {
    final session = JenkinsArtifactSession(
      store: TokenStore(),
      client: JenkinsClient(
        client: MockClient((_) async => http.Response('', 200)),
      ),
      stager: ArtifactStagingService(),
    );

    final result = await session.restoreDetailed();

    expect(result.kind, JenkinsSessionRestoreKind.missing);
    expect(result.isRestored, isFalse);
  });

  test(
    'restoreDetailed returns restored after current user validation',
    () async {
      FlutterSecureStorage.setMockInitialValues({
        'jenkins.baseUrl': baseUrl,
        'jenkins.username': credentials.username,
        'jenkins.apiToken': credentials.apiToken,
      });

      final session = JenkinsArtifactSession(
        store: TokenStore(),
        client: JenkinsClient(
          client: MockClient((request) async {
            if (request.url.path.endsWith('whoAmI/api/json')) {
              return http.Response(jsonEncode({'id': 'user'}), 200);
            }
            return http.Response('unexpected', 400);
          }),
        ),
        stager: ArtifactStagingService(),
      );

      final result = await session.restoreDetailed();

      expect(result.kind, JenkinsSessionRestoreKind.restored);
      expect(result.isRestored, isTrue);
      expect(session.isRestored, isTrue);
    },
  );

  test(
    'restoreDetailed returns reauthRequired and clears session on 401',
    () async {
      FlutterSecureStorage.setMockInitialValues({
        'jenkins.baseUrl': baseUrl,
        'jenkins.username': credentials.username,
        'jenkins.apiToken': credentials.apiToken,
      });

      final store = TokenStore();
      final session = JenkinsArtifactSession(
        store: store,
        client: JenkinsClient(
          client: MockClient((_) async => http.Response('Unauthorized', 401)),
        ),
        stager: ArtifactStagingService(),
      );

      final result = await session.restoreDetailed();

      expect(result.kind, JenkinsSessionRestoreKind.reauthRequired);
      expect(result.isRestored, isFalse);
      expect(session.isRestored, isFalse);
      expect(await store.readSession(), isNull);
    },
  );

  test(
    'restoreDetailed returns reauthRequired and clears session on 403',
    () async {
      FlutterSecureStorage.setMockInitialValues({
        'jenkins.baseUrl': baseUrl,
        'jenkins.username': credentials.username,
        'jenkins.apiToken': credentials.apiToken,
      });

      final store = TokenStore();
      final session = JenkinsArtifactSession(
        store: store,
        client: JenkinsClient(
          client: MockClient((_) async => http.Response('Forbidden', 403)),
        ),
        stager: ArtifactStagingService(),
      );

      final result = await session.restoreDetailed();

      expect(result.kind, JenkinsSessionRestoreKind.reauthRequired);
      expect(result.isRestored, isFalse);
      expect(session.isRestored, isFalse);
      expect(await store.readSession(), isNull);
    },
  );

  test(
    'restoreDetailed returns reauthRequired and clears session on anonymous whoAmI',
    () async {
      FlutterSecureStorage.setMockInitialValues({
        'jenkins.baseUrl': baseUrl,
        'jenkins.username': credentials.username,
        'jenkins.apiToken': credentials.apiToken,
      });

      final store = TokenStore();
      final session = JenkinsArtifactSession(
        store: store,
        client: JenkinsClient(
          client: MockClient(
            (_) async => http.Response(
              jsonEncode({
                'anonymous': true,
                'id': 'anonymous',
                'fullName': 'anonymous',
              }),
              200,
            ),
          ),
        ),
        stager: ArtifactStagingService(),
      );

      final result = await session.restoreDetailed();

      expect(result.kind, JenkinsSessionRestoreKind.reauthRequired);
      expect(result.isRestored, isFalse);
      expect(session.isRestored, isFalse);
      expect(await store.readSession(), isNull);
    },
  );

  test(
    'restoreDetailed returns networkFailure and keeps session on IOException',
    () async {
      FlutterSecureStorage.setMockInitialValues({
        'jenkins.baseUrl': baseUrl,
        'jenkins.username': credentials.username,
        'jenkins.apiToken': credentials.apiToken,
      });

      final store = TokenStore();
      final session = JenkinsArtifactSession(
        store: store,
        client: JenkinsClient(
          client: MockClient(
            (_) async => throw const SocketException('offline'),
          ),
        ),
        stager: ArtifactStagingService(),
      );

      final result = await session.restoreDetailed();

      expect(result.kind, JenkinsSessionRestoreKind.networkFailure);
      expect(result.isRestored, isFalse);
      expect(session.isRestored, isFalse);
      expect(await store.readSession(), isNotNull);
    },
  );

  test(
    'restoreDetailed returns serverFailure and keeps session on non-auth JenkinsClientException',
    () async {
      FlutterSecureStorage.setMockInitialValues({
        'jenkins.baseUrl': baseUrl,
        'jenkins.username': credentials.username,
        'jenkins.apiToken': credentials.apiToken,
      });

      final store = TokenStore();
      final session = JenkinsArtifactSession(
        store: store,
        client: JenkinsClient(
          client: MockClient(
            (_) async => http.Response('Internal Server Error', 500),
          ),
        ),
        stager: ArtifactStagingService(),
      );

      final result = await session.restoreDetailed();

      expect(result.kind, JenkinsSessionRestoreKind.serverFailure);
      expect(result.isRestored, isFalse);
      expect(session.isRestored, isFalse);
      expect(await store.readSession(), isNotNull);
    },
  );

  test('restore bool wrapper remains true only for restored', () async {
    FlutterSecureStorage.setMockInitialValues({
      'jenkins.baseUrl': baseUrl,
      'jenkins.username': credentials.username,
      'jenkins.apiToken': credentials.apiToken,
    });

    final session = JenkinsArtifactSession(
      store: TokenStore(),
      client: JenkinsClient(
        client: MockClient((request) async {
          if (request.url.path.endsWith('whoAmI/api/json')) {
            return http.Response(jsonEncode({'id': 'user'}), 200);
          }
          return http.Response('unexpected', 400);
        }),
      ),
      stager: ArtifactStagingService(),
    );

    expect(await session.restore(), isTrue);

    FlutterSecureStorage.setMockInitialValues({});
    final sessionMissing = JenkinsArtifactSession(
      store: TokenStore(),
      client: JenkinsClient(
        client: MockClient((_) async => http.Response('', 200)),
      ),
      stager: ArtifactStagingService(),
    );
    expect(await sessionMissing.restore(), isFalse);
  });
}
