/// Datasource distant pour le workflow financier (API)
library finance_workflow_remote_datasource;

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:injectable/injectable.dart';
import '../../../../core/config/environment.dart';
import '../../../../core/error/exceptions.dart';
import '../../../authentication/data/datasources/keycloak_auth_service.dart';
import '../models/transaction_approval_model.dart';
import '../models/budget_model.dart';

@lazySingleton
class FinanceWorkflowRemoteDatasource {
  final http.Client client;
  final KeycloakAuthService authService;

  FinanceWorkflowRemoteDatasource({
    required this.client,
    required this.authService,
  });

  /// Headers HTTP avec authentification — rafraîchit le token si expiré (fix IC-03)
  Future<Map<String, String>> _getHeaders() async {
    final token = await authService.getValidAccessToken();
    return {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      if (token != null) 'Authorization': 'Bearer $token',
    };
  }

  // === APPROBATIONS ===

  Future<List<TransactionApprovalModel>> getPendingApprovals({
    String? organizationId,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/approvals/pending')
        .replace(queryParameters: {
      if (organizationId != null) 'organizationId': organizationId,
    });

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      final List<dynamic> jsonList = json.decode(response.body);
      return jsonList
          .map((json) => TransactionApprovalModel.fromJson(json))
          .toList();
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération des approbations');
    }
  }

  Future<TransactionApprovalModel> getApprovalById(String approvalId) async {
    final uri = Uri.parse(
        '${AppConfig.apiBaseUrl}/api/finance/approvals/$approvalId');

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      return TransactionApprovalModel.fromJson(json.decode(response.body));
    } else if (response.statusCode == 404) {
      throw NotFoundException('Approbation non trouvée');
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération de l\'approbation');
    }
  }

  Future<TransactionApprovalModel> approveTransaction({
    required String approvalId,
    String? comment,
  }) async {
    final uri =
        Uri.parse('${AppConfig.apiBaseUrl}/api/finance/approvals/$approvalId/approve');

    final body = json.encode({
      if (comment != null) 'comment': comment,
    });

    final response = await client.post(
      uri,
      headers: await _getHeaders(),
      body: body,
    );

    if (response.statusCode == 200) {
      return TransactionApprovalModel.fromJson(json.decode(response.body));
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else if (response.statusCode == 403) {
      throw ForbiddenException('Permission insuffisante pour approuver');
    } else {
      throw ServerException('Erreur lors de l\'approbation');
    }
  }

  Future<TransactionApprovalModel> rejectTransaction({
    required String approvalId,
    required String reason,
  }) async {
    final uri =
        Uri.parse('${AppConfig.apiBaseUrl}/api/finance/approvals/$approvalId/reject');

    final body = json.encode({
      'reason': reason,
    });

    final response = await client.post(
      uri,
      headers: await _getHeaders(),
      body: body,
    );

    if (response.statusCode == 200) {
      return TransactionApprovalModel.fromJson(json.decode(response.body));
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else if (response.statusCode == 403) {
      throw ForbiddenException('Permission insuffisante pour rejeter');
    } else {
      throw ServerException('Erreur lors du rejet');
    }
  }

  Future<TransactionApprovalModel> requestApproval({
    required String transactionId,
    required String transactionType,
    required double amount,
    String? organizationId,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/approvals');

    final body = json.encode({
      'transactionId': transactionId,
      'transactionType': transactionType,
      'amount': amount,
      if (organizationId != null) 'organizationId': organizationId,
    });

    final response = await client.post(
      uri,
      headers: await _getHeaders(),
      body: body,
    );

    if (response.statusCode == 201 || response.statusCode == 200) {
      return TransactionApprovalModel.fromJson(json.decode(response.body));
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else if (response.statusCode == 400) {
      throw ValidationException('Données invalides');
    } else {
      throw ServerException('Erreur lors de la demande d\'approbation');
    }
  }

  Future<List<TransactionApprovalModel>> getApprovalsHistory({
    required String organizationId,
    DateTime? startDate,
    DateTime? endDate,
    String? status,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/approvals/history')
        .replace(queryParameters: {
      'organizationId': organizationId,
      if (startDate != null) 'startDate': startDate.toIso8601String(),
      if (endDate != null) 'endDate': endDate.toIso8601String(),
      if (status != null) 'status': status,
    });

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      final List<dynamic> jsonList = json.decode(response.body);
      return jsonList
          .map((json) => TransactionApprovalModel.fromJson(json))
          .toList();
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération de l\'historique');
    }
  }

  // === BUDGETS ===

  Future<List<BudgetModel>> getBudgets({
    String? organizationId,
    String? status,
    int? year,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/budgets')
        .replace(queryParameters: {
      if (organizationId != null) 'organizationId': organizationId,
      if (status != null) 'status': status,
      if (year != null) 'year': year.toString(),
    });

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      final List<dynamic> jsonList = json.decode(response.body);
      return jsonList.map((json) => BudgetModel.fromJson(json)).toList();
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération des budgets');
    }
  }

  Future<BudgetModel> getBudgetById(String budgetId) async {
    final uri =
        Uri.parse('${AppConfig.apiBaseUrl}/api/finance/budgets/$budgetId');

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      return BudgetModel.fromJson(json.decode(response.body));
    } else if (response.statusCode == 404) {
      throw NotFoundException('Budget non trouvé');
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération du budget');
    }
  }

  Future<BudgetModel> createBudget({
    required String name,
    String? description,
    required String organizationId,
    required String period,
    required int year,
    int? month,
    required List<Map<String, dynamic>> lines,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/budgets');

    final body = json.encode({
      'name': name,
      if (description != null) 'description': description,
      'organizationId': organizationId,
      'period': period,
      'year': year,
      if (month != null) 'month': month,
      'lines': lines,
    });

    final response = await client.post(
      uri,
      headers: await _getHeaders(),
      body: body,
    );

    if (response.statusCode == 201 || response.statusCode == 200) {
      return BudgetModel.fromJson(json.decode(response.body));
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else if (response.statusCode == 403) {
      throw ForbiddenException('Permission insuffisante pour créer un budget');
    } else {
      throw ServerException('Erreur lors de la création du budget');
    }
  }

  Future<Map<String, dynamic>> getBudgetTracking({
    required String budgetId,
  }) async {
    final uri = Uri.parse(
        '${AppConfig.apiBaseUrl}/api/finance/budgets/$budgetId/tracking');

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      return json.decode(response.body) as Map<String, dynamic>;
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération du suivi budgétaire');
    }
  }

  Future<BudgetModel> updateBudget({
    required String budgetId,
    required Map<String, dynamic> updates,
  }) async {
    final uri =
        Uri.parse('${AppConfig.apiBaseUrl}/api/finance/budgets/$budgetId');

    final body = json.encode(updates);

    final response = await client.put(
      uri,
      headers: await _getHeaders(),
      body: body,
    );

    if (response.statusCode == 200) {
      return BudgetModel.fromJson(json.decode(response.body));
    } else if (response.statusCode == 404) {
      throw NotFoundException('Budget non trouvé');
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else if (response.statusCode == 400) {
      throw ValidationException('Données invalides');
    } else {
      throw ServerException('Erreur lors de la mise à jour du budget');
    }
  }

  Future<void> deleteBudget({required String budgetId}) async {
    final uri =
        Uri.parse('${AppConfig.apiBaseUrl}/api/finance/budgets/$budgetId');

    final response = await client.delete(uri, headers: await _getHeaders());

    if (response.statusCode != 204 && response.statusCode != 200) {
      if (response.statusCode == 404) {
        throw NotFoundException('Budget non trouvé');
      } else if (response.statusCode == 401) {
        throw UnauthorizedException();
      } else {
        throw ServerException('Erreur lors de la suppression du budget');
      }
    }
  }

  // === WORKFLOW STATS & AUDIT ===

  Future<Map<String, dynamic>> getWorkflowStats({
    String? organizationId,
    DateTime? startDate,
    DateTime? endDate,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/stats')
        .replace(queryParameters: {
      if (organizationId != null) 'organizationId': organizationId,
      if (startDate != null) 'startDate': startDate.toIso8601String(),
      if (endDate != null) 'endDate': endDate.toIso8601String(),
    });

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      return json.decode(response.body) as Map<String, dynamic>;
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération des statistiques');
    }
  }

  Future<List<Map<String, dynamic>>> getAuditLogs({
    String? organizationId,
    DateTime? startDate,
    DateTime? endDate,
    String? operation,
    String? entityType,
    String? severity,
    int limit = 100,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/audit-logs')
        .replace(queryParameters: {
      if (organizationId != null) 'organizationId': organizationId,
      if (startDate != null) 'startDate': startDate.toIso8601String(),
      if (endDate != null) 'endDate': endDate.toIso8601String(),
      if (operation != null) 'operation': operation,
      if (entityType != null) 'entityType': entityType,
      if (severity != null) 'severity': severity,
      'limit': limit.toString(),
    });

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      final List<dynamic> jsonList = json.decode(response.body);
      return jsonList.map((e) => e as Map<String, dynamic>).toList();
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération des logs d\'audit');
    }
  }

  Future<List<Map<String, dynamic>>> getAnomalies({
    String? organizationId,
    DateTime? startDate,
    DateTime? endDate,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/audit-logs/anomalies')
        .replace(queryParameters: {
      if (organizationId != null) 'organizationId': organizationId,
      if (startDate != null) 'startDate': startDate.toIso8601String(),
      if (endDate != null) 'endDate': endDate.toIso8601String(),
    });

    final response = await client.get(uri, headers: await _getHeaders());

    if (response.statusCode == 200) {
      final List<dynamic> jsonList = json.decode(response.body);
      return jsonList.map((e) => e as Map<String, dynamic>).toList();
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de la récupération des anomalies');
    }
  }

  Future<Map<String, dynamic>> exportAuditLogs({
    String? organizationId,
    String format = 'csv',
    DateTime? startDate,
    DateTime? endDate,
  }) async {
    final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/finance/audit-logs/export');

    final body = json.encode({
      if (organizationId != null) 'organizationId': organizationId,
      'format': format,
      if (startDate != null) 'startDate': startDate.toIso8601String(),
      if (endDate != null) 'endDate': endDate.toIso8601String(),
    });

    final response = await client.post(
      uri,
      headers: await _getHeaders(),
      body: body,
    );

    if (response.statusCode == 200) {
      return json.decode(response.body) as Map<String, dynamic>;
    } else if (response.statusCode == 401) {
      throw UnauthorizedException();
    } else {
      throw ServerException('Erreur lors de l\'export des logs d\'audit');
    }
  }
}
