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

import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:service/constant/box_name.dart';
import 'package:service/constant/links.dart';
import 'package:service/controller/functions/encrypt_decrypt.dart';
import 'package:service/env/env.dart';
import 'package:service/main.dart';
import 'package:service/print.dart';

import '../../constant/api_key.dart';

class CRUD {
  static bool _isRefreshingJWT = false;
  static String _lastErrorSignature = '';
  static DateTime _lastErrorTimestamp = DateTime(2000);
  static const Duration _errorLogDebounceDuration = Duration(minutes: 1);

  // ── JWT Validity Check (No external libs) ──────────────────────
  static bool _isJwtValid(String? token) {
    if (token == null || token.isEmpty) return false;
    try {
      final parts = token.split('.');
      if (parts.length != 3) return false;
      String payload = parts[1];
      switch (payload.length % 4) {
        case 2:
          payload += '==';
          break;
        case 3:
          payload += '=';
          break;
      }
      final decoded = jsonDecode(utf8.decode(base64Url.decode(payload)));
      final exp = decoded['exp'];
      if (exp == null) return false;
      // 30 seconds buffer
      return DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 30000);
    } catch (_) {
      return false;
    }
  }

  static Future<void> addError(
      String error, String details, String where) async {
    try {
      final currentErrorSignature = '$where-$error';
      final now = DateTime.now();

      if (currentErrorSignature == _lastErrorSignature &&
          now.difference(_lastErrorTimestamp) < _errorLogDebounceDuration) {
        return;
      }

      _lastErrorSignature = currentErrorSignature;
      _lastErrorTimestamp = now;

      final userId =
          box.read(BoxName.driverID) ?? box.read(BoxName.passengerID);
      final userType = 'Service';
      final phone = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);

      CRUD().post(
        link: AppLink.addError,
        payload: {
          'error': error.toString(),
          'userId': userId.toString(),
          'userType': userType,
          'phone': phone.toString(),
          'device': where,
          'details': details,
        },
      );
    } catch (e) {}
  }

  String _getFpHeader() {
    return box.read(BoxName.fingerPrint)?.toString() ?? '';
  }

  // ═══════════════════════════════════════════════════════════════
  // _makeRequest — Central Request Handler
  // ───────────────────────────────────────────────────────────────
  Future<dynamic> _makeRequest({
    required String link,
    Map<String, dynamic>? payload,
    required Map<String, String> headers,
  }) async {
    const totalTimeout = Duration(seconds: 60);

    Future<http.Response> doPost() {
      final url = Uri.parse(link);
      return http
          .post(url, body: payload, headers: headers)
          .timeout(totalTimeout);
    }

    http.Response? response;
    int attempts = 0;
    final requestId =
        DateTime.now().millisecondsSinceEpoch.toString().substring(7);

    Log.print('🚀 [REQ-$requestId] $link');
    Log.print('🔑 [FP-$requestId] ${headers['X-Device-FP']}');
    if (payload != null) Log.print('📦 [PAYLOAD-$requestId] $payload');


    while (attempts < 3) {
      try {
        attempts++;
        response = await doPost();
        break;
      } on SocketException catch (_) {
        Log.print('⚠️ SocketException attempt $attempts — $link');
        if (attempts >= 3) return 'no_internet';
        await Future.delayed(const Duration(seconds: 1));
      } on TimeoutException catch (_) {
        Log.print('⚠️ TimeoutException attempt $attempts — $link');
        if (attempts >= 3) return 'failure';
      } catch (e) {
        if (e.toString().contains('errno = 9') && attempts < 3) {
          await Future.delayed(const Duration(milliseconds: 500));
          continue;
        }
        addError(
            'HTTP Exception: $e', 'Try: $attempts', 'CRUD._makeRequest $link');
        return 'failure';
      }
    }

    if (response == null) return 'failure';

    final sc = response.statusCode;
    final body = response.body;

    Log.print('📥 [RES-$requestId] [$sc] $link');
    Log.print('📄 [BODY-$requestId] $body');

    if (sc >= 200 && sc < 300) {
      try {
        return jsonDecode(body);
      } catch (e, st) {
        addError(
            'JSON Decode Error', 'Body: $body\n$st', 'CRUD._makeRequest $link');
        return 'failure';
      }
    }

    if (sc == 401) {
      if (!_isRefreshingJWT && !link.contains('errorApp.php')) {
        _isRefreshingJWT = true;
        try {
          await getJWT();
        } finally {
          _isRefreshingJWT = false;
        }
      }
      return 'token_expired';
    }

    if (sc >= 500) {
      addError('Server 5xx', 'SC: $sc\nBody: $body', 'CRUD._makeRequest $link');
      return 'failure';
    }

    return 'failure';
  }

  // ═══════════════════════════════════════════════════════════════
  // post — standard POST
  // ═══════════════════════════════════════════════════════════════
  Future<dynamic> post({
    required String link,
    Map<String, dynamic>? payload,
  }) async {
    String token = r(box.read(BoxName.jwt) ?? '').toString().split(Env.addd)[0];

    if (!_isJwtValid(token) &&
        !_isRefreshingJWT &&
        !link.contains('login.php')) {
      _isRefreshingJWT = true;
      try {
        await getJWT();
        token = r(box.read(BoxName.jwt) ?? '').toString().split(Env.addd)[0];
      } finally {
        _isRefreshingJWT = false;
      }
    }

    final headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Authorization': 'Bearer $token',
      'X-Device-FP': _getFpHeader(),
    };

    return await _makeRequest(link: link, payload: payload, headers: headers);
  }

  // ═══════════════════════════════════════════════════════════════
  // get — standard GET (uses POST method in this architecture)
  // ═══════════════════════════════════════════════════════════════
  Future<dynamic> get({
    required String link,
    Map<String, dynamic>? payload,
  }) async {
    return await post(link: link, payload: payload);
  }

  // ═══════════════════════════════════════════════════════════════
  // getJWT — V1 Login Flow
  // ═══════════════════════════════════════════════════════════════
  Future<void> getJWT() async {
    var payload = {
      'fingerprint': _getFpHeader(),
      'password': box.read(BoxName.password) ?? '',
      'aud': 'service',
    };

    final headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
    };

    final response = await _makeRequest(
        link: AppLink.login, payload: payload, headers: headers);

    if (response != 'failure' &&
        response is Map &&
        response['status'] == 'success') {
      final jwt = response['message']['jwt'];
      Log.print('jwt: $jwt');
      box.write(BoxName.jwt, c(jwt));
    }
  }

  // ─────────────────────────────────────────────────────────────
  // Service Specific Methods (Preserved)
  // ─────────────────────────────────────────────────────────────

  Future<dynamic> arabicTextExtractByVisionAndAI({
    required String imagePath,
    required String driverID,
  }) async {
    var headers = {
      'Content-Type': 'application/json',
      'Ocp-Apim-Subscription-Key': AK.ocpApimSubscriptionKey
    };

    String imagePathFull =
        '${AppLink.server}/card_image/$imagePath-$driverID.jpg';
    var request = http.Request(
        'POST',
        Uri.parse(
            'https://eastus.api.cognitive.microsoft.com/computervision/imageanalysis:analyze?features=caption,read&model-version=latest&language=en&api-version=2024-02-01'));
    request.body = json.encode({"url": imagePathFull});
    request.headers.addAll(headers);

    http.StreamedResponse response = await request.send();

    if (response.statusCode == 200) {
      return await response.stream.bytesToString();
    }
    return 'failure';
  }

  Future<dynamic> getAgoraToken({
    required String channelName,
    required String uid,
  }) async {
    var res = await http.get(
        Uri.parse(
            'https://orca-app-b2i85.ondigitalocean.app/token?channelName=$channelName'),
        headers: {'Authorization': 'Bearer '});

    if (res.statusCode == 200) {
      var response = jsonDecode(res.body);
      return response['token'];
    }
    return 'failure';
  }

  Future<dynamic> getLlama({
    required String link,
    required String payload,
    required String prompt,
  }) async {
    var url = Uri.parse(link);
    var headers = {
      'Content-Type': 'application/json',
      'Authorization':
          'Bearer LL-X5lJ0Px9CzKK0HTuVZ3u2u4v3tGWkImLTG7okGRk4t25zrsLqJ0qNoUzZ2x4ciPy'
    };
    var data = json.encode({
      "model": "Llama-3-70b-Inst-FW",
      "messages": [
        {
          "role": "user",
          "content":
              "Extract the desired information from the following passage as json decoded like $prompt just in this:\n\n$payload"
        }
      ],
      "temperature": 0.9
    });
    var response = await http.post(url, body: data, headers: headers);

    if (response.statusCode == 200) {
      return response.body;
    }
    return response.statusCode;
  }

  sendEmail(String link, Map<String, String>? payload) async {
    var headers = {
      "Content-Type": "application/x-www-form-urlencoded",
      'Authorization':
          'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
    };
    var request = http.Request('POST', Uri.parse(link));
    request.bodyFields = payload!;
    request.headers.addAll(headers);

    http.StreamedResponse response = await request.send();
    if (response.statusCode == 200) {}
  }
}
