import 'dart:convert';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;

import 'api_exception.dart';

/// Platform-neutral HTTP client.
/// API base URL is injected at build time via --dart-define=API_BASE_URL.
/// Default is same-origin '/api' to avoid mixed-content on HTTPS sites.
class ApiClient {
  final String baseUrl;
  final http.Client _client;

  ApiClient({http.Client? client})
      : baseUrl = const String.fromEnvironment(
          'API_BASE_URL',
          defaultValue: '/api',
        ),
        _client = client ?? http.Client();

  Map<String, String> _headers({String? token}) => {
        'Content-Type': 'application/json',
        if (token != null) 'Authorization': 'Bearer $token',
      };

  Future<dynamic> getJson(String path, {String? token}) async {
    final response = await _client.get(
      Uri.parse('$baseUrl$path'),
      headers: _headers(token: token),
    );
    return _decodeOrNull(_guardResponse(response));
  }

  Future<dynamic> postJson(String path, {Object? body, String? token}) async {
    final response = await _client.post(
      Uri.parse('$baseUrl$path'),
      headers: _headers(token: token),
      body: body == null ? null : jsonEncode(body),
    );
    return _decodeOrNull(_guardResponse(response));
  }

  Future<dynamic> putJson(String path, {Object? body, String? token}) async {
    final response = await _client.put(
      Uri.parse('$baseUrl$path'),
      headers: _headers(token: token),
      body: body == null ? null : jsonEncode(body),
    );
    return _decodeOrNull(_guardResponse(response));
  }

  Future<dynamic> patchJson(String path, {Object? body, String? token}) async {
    final response = await _client.patch(
      Uri.parse('$baseUrl$path'),
      headers: _headers(token: token),
      body: body == null ? null : jsonEncode(body),
    );
    return _decodeOrNull(_guardResponse(response));
  }

  Future<dynamic> deleteJson(String path, {String? token}) async {
    final response = await _client.delete(
      Uri.parse('$baseUrl$path'),
      headers: _headers(token: token),
    );
    return _decodeOrNull(_guardResponse(response));
  }

  http.Response _guardResponse(http.Response response) {
    if (response.statusCode >= 200 && response.statusCode < 300) {
      return response;
    }

    final parsedBody = _decodeOrNull(response);
    final serverMessage = _extractMessage(parsedBody);

    if (response.statusCode == 401) {
      throw ApiException(
        type: ApiErrorType.unauthorized,
        statusCode: 401,
        message: serverMessage ?? 'Unauthorized',
      );
    }

    if (response.statusCode == 403) {
      throw ApiException(
        type: ApiErrorType.forbidden,
        statusCode: 403,
        message: serverMessage ?? 'Forbidden',
      );
    }

    if (response.statusCode >= 500) {
      throw ApiException(
        type: ApiErrorType.server,
        statusCode: response.statusCode,
        message: serverMessage ?? 'Server error',
      );
    }

    throw ApiException(
      type: ApiErrorType.unknown,
      statusCode: response.statusCode,
      message: serverMessage ?? 'Request failed',
    );
  }

  dynamic _decodeOrNull(http.Response response) {
    final body = response.body.trim();
    if (body.isEmpty) return null;
    try {
      return jsonDecode(body);
    } catch (_) {
      return body;
    }
  }

  String? _extractMessage(dynamic parsedBody) {
    if (parsedBody is Map<String, dynamic>) {
      final message = parsedBody['message'];
      if (message is String && message.trim().isNotEmpty) return message;
      if (message is List && message.isNotEmpty) {
        return message.first.toString();
      }
      final error = parsedBody['error'];
      if (error is String && error.trim().isNotEmpty) return error;
    }
    if (parsedBody is String && parsedBody.trim().isNotEmpty) return parsedBody;
    return null;
  }
}

final apiClientProvider = Provider<ApiClient>((ref) {
  return ApiClient();
});
