import 'dart:io';
import 'dart:convert';
import 'dart:math';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dio/dio.dart';
import 'package:island/pods/network.dart';
import 'package:island/talker.dart';

class WebAuthServer {
  final Ref _ref;
  HttpServer? _server;
  String? _challenge;
  DateTime? _challengeTimestamp;

  final _challengeTtl = const Duration(seconds: 30);

  WebAuthServer(this._ref);

  Future<int> start() async {
    if (_server != null) {
      talker.warning('Web auth server already running.');
      return _server!.port;
    }

    final port = await _findUnusedPort(40000, 41000);
    _server = await HttpServer.bind(InternetAddress.loopbackIPv4, port);
    talker.info('Web auth server started on http://127.0.0.1:$port');

    _server!.listen(_handleRequest);
    return port;
  }

  void stop() {
    _server?.close(force: true);
    _server = null;
    talker.info('Web auth server stopped.');
  }

  Future<int> _findUnusedPort(int start, int end) async {
    for (var port = start; port <= end; port++) {
      try {
        var socket = await ServerSocket.bind(InternetAddress.loopbackIPv4, port);
        await socket.close();
        return port;
      } catch (e) {
        // Port is in use, try next
      }
    }
    throw Exception('No unused port found in range $start-$end');
  }

  String _generateChallenge() {
    final random = Random.secure();
    final values = List<int>.generate(32, (i) => random.nextInt(256));
    return base64Url.encode(values);
  }

  void _addCorsHeaders(HttpResponse response) {
    const webUrl = 'https://app.solian.fr'; 

    response.headers.add('Access-Control-Allow-Origin', webUrl);
    response.headers.add('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    response.headers.add('Access-Control-Allow-Headers', '*');
  }

  Future<void> _handleRequest(HttpRequest request) async {
    try {
      _addCorsHeaders(request.response);

      if (request.method == 'OPTIONS') {
        request.response.statusCode = HttpStatus.noContent;
        await request.response.close();
        return;
      }

      talker.info('Web auth request: ${request.method} ${request.uri.path}');

      if (request.method == 'GET' && request.uri.path == '/alive') {
        await _handleAlive(request);
      } else if (request.method == 'POST' && request.uri.path == '/exchange') {
        await _handleExchange(request);
      } else {
        request.response.statusCode = HttpStatus.notFound;
        request.response.write(jsonEncode({'error': 'Not Found'}));
        await request.response.close();
      }
    } catch (e, st) {
      talker.handle(e, st, 'Error handling web auth request');
      try {
        request.response.statusCode = HttpStatus.internalServerError;
        request.response.write(jsonEncode({'error': 'Internal Server Error'}));
        await request.response.close();
      } catch (e2) {
        talker.error('Failed to send error response: $e2');
      }
    }
  }

  Future<void> _handleAlive(HttpRequest request) async {
    _challenge = _generateChallenge();
    _challengeTimestamp = DateTime.now();

    final response = {
      'status': 'ok',
      'challenge': _challenge,
    };

    request.response.statusCode = HttpStatus.ok;
    request.response.headers.contentType = ContentType.json;
    request.response.write(jsonEncode(response));
    await request.response.close();
  }

  Future<void> _handleExchange(HttpRequest request) async {
    if (_challenge == null ||
        _challengeTimestamp == null ||
        DateTime.now().difference(_challengeTimestamp!) > _challengeTtl) {
      request.response.statusCode = HttpStatus.badRequest;
      request.response.write(jsonEncode({
        'error': 'Invalid or expired challenge. Please call /alive first.'
      }));
      await request.response.close();
      return;
    }

    final requestBody = await utf8.decodeStream(request);
    final Map<String, dynamic> data;
    try {
      data = jsonDecode(requestBody);
    } catch (e) {
      request.response.statusCode = HttpStatus.badRequest;
      request.response.write(jsonEncode({'error': 'Invalid JSON body'}));
      await request.response.close();
      return;
    }

    final String? signedChallenge = data['signedChallenge'];
    final Map<String, dynamic>? deviceInfo = data['deviceInfo'];

    if (signedChallenge == null) {
      request.response.statusCode = HttpStatus.badRequest;
      request.response.write(jsonEncode({'error': 'Missing signedChallenge'}));
      await request.response.close();
      return;
    }

    final currentChallenge = _challenge!;
    _challenge = null;
    _challengeTimestamp = null;

    try {
      final dio = _ref.read(apiClientProvider);
      
      final response = await dio.post(
        '/pass/auth/login/session',
        data: {
          'signedChallenge': signedChallenge,
          'challenge': currentChallenge,
          ...?deviceInfo,
        },
      );

      if (response.statusCode == 200 && response.data != null) {
        final webToken = response.data['token'];
        request.response.statusCode = HttpStatus.ok;
        request.response.write(jsonEncode({'token': webToken}));
      } else {
        throw Exception(
            'Backend exchange failed with status ${response.statusCode}');
      }
    } on DioException catch (e) {
      talker.error('Backend exchange failed: ${e.response?.data}');
      request.response.statusCode =
          e.response?.statusCode ?? HttpStatus.internalServerError;
      request.response.write(
          jsonEncode(e.response?.data ?? {'error': 'Backend communication failed'}));
    } catch (e, st) {
      talker.handle(e, st, 'Error during backend exchange');
      request.response.statusCode = HttpStatus.internalServerError;
      request.response
          .write(jsonEncode({'error': 'An unexpected error occurred'}));
    } finally {
      await request.response.close();
    }
  }
}
