import 'dart:convert';
import 'dart:typed_data';

import 'package:dio/dio.dart';

import 'package:superport_v2/core/network/api_client.dart';
import 'package:superport_v2/core/network/api_routes.dart';
import 'package:superport_v2/features/reporting/domain/entities/report_download_result.dart';
import 'package:superport_v2/features/reporting/domain/entities/report_export_format.dart';
import 'package:superport_v2/features/reporting/domain/entities/report_export_request.dart';
import 'package:superport_v2/features/reporting/domain/repositories/reporting_repository.dart';

/// 보고서 다운로드 API를 호출하는 원격 리포지토리 구현체.
class ReportingRepositoryRemote implements ReportingRepository {
  ReportingRepositoryRemote({required ApiClient apiClient}) : _api = apiClient;

  final ApiClient _api;

  static const _transactionsPath =
      '${ApiRoutes.apiV1}/reports/transactions/export';
  static const _approvalsPath = '${ApiRoutes.apiV1}/reports/approvals/export';

  @override
  Future<ReportDownloadResult> exportTransactions(
    ReportExportRequest request,
  ) async {
    final response = await _api.get<Uint8List>(
      _transactionsPath,
      query: _buildTransactionQuery(request),
      options: Options(responseType: ResponseType.bytes),
    );
    return _mapResponse(response, format: request.format);
  }

  @override
  Future<ReportDownloadResult> exportApprovals(
    ReportExportRequest request,
  ) async {
    final response = await _api.get<Uint8List>(
      _approvalsPath,
      query: _buildApprovalQuery(request),
      options: Options(responseType: ResponseType.bytes),
    );
    return _mapResponse(response, format: request.format);
  }

  Map<String, dynamic> _buildTransactionQuery(ReportExportRequest request) {
    return {
      'from': _formatDateOnly(request.from),
      'to': _formatDateOnly(request.to),
      'format': request.format.apiValue,
      if (request.transactionStatusId != null)
        'transaction_status_id': request.transactionStatusId,
      if (request.approvalStatusId != null)
        'approval_status_id': request.approvalStatusId,
      if (request.requestedById != null)
        'requested_by_id': request.requestedById,
    };
  }

  Map<String, dynamic> _buildApprovalQuery(ReportExportRequest request) {
    return {
      'from': request.from.toIso8601String(),
      'to': request.to.toIso8601String(),
      'format': request.format.apiValue,
      if (request.transactionStatusId != null)
        'transaction_status_id': request.transactionStatusId,
      if (request.approvalStatusId != null)
        'approval_status_id': request.approvalStatusId,
      if (request.requestedById != null)
        'requested_by_id': request.requestedById,
    };
  }

  /// 백엔드 트랜잭션 보고서가 요구하는 `yyyy-MM-dd` 형식으로 변환한다.
  String _formatDateOnly(DateTime value) {
    final iso = value.toIso8601String();
    final separatorIndex = iso.indexOf('T');
    if (separatorIndex == -1) {
      return iso;
    }
    return iso.substring(0, separatorIndex);
  }

  ReportDownloadResult _mapResponse(
    Response<Uint8List> response, {
    required ReportExportFormat format,
  }) {
    final contentType = response.headers.value('content-type') ?? '';
    final disposition = response.headers.value('content-disposition') ?? '';

    if (contentType.contains('application/json')) {
      final body = _decodeJson(response.data);
      final map = body is Map<String, dynamic> ? body : <String, dynamic>{};
      final data = map['data'] is Map<String, dynamic>
          ? map['data'] as Map<String, dynamic>
          : map;
      final url = _readString(data, 'download_url');
      return ReportDownloadResult(
        downloadUrl: url == null ? null : Uri.tryParse(url),
        filename: _readString(data, 'filename'),
        mimeType: _readString(data, 'mime_type') ?? _defaultMimeType(format),
        expiresAt: _parseDateTime(_readString(data, 'expires_at')),
      );
    }

    final filename = _parseFilename(disposition) ?? 'report.${format.apiValue}';
    final bytes = response.data ?? Uint8List(0);

    return ReportDownloadResult(
      bytes: bytes,
      filename: filename,
      mimeType: contentType.isEmpty ? _defaultMimeType(format) : contentType,
    );
  }

  dynamic _decodeJson(Uint8List? bytes) {
    if (bytes == null || bytes.isEmpty) {
      return const <String, dynamic>{};
    }
    try {
      final decoded = utf8.decode(bytes);
      return jsonDecode(decoded);
    } catch (_) {
      return const <String, dynamic>{};
    }
  }

  String? _readString(Map<String, dynamic> map, String key) {
    final value = map[key];
    if (value is String && value.trim().isNotEmpty) {
      return value.trim();
    }
    return null;
  }

  DateTime? _parseDateTime(String? value) {
    if (value == null || value.isEmpty) {
      return null;
    }
    return DateTime.tryParse(value);
  }

  String? _parseFilename(String disposition) {
    if (disposition.isEmpty) {
      return null;
    }
    final parts = disposition.split(';');
    for (final part in parts) {
      final trimmed = part.trim();
      if (trimmed.toLowerCase().startsWith('filename=')) {
        final encoded = trimmed.substring('filename='.length);
        return encoded.replaceAll('"', '');
      }
    }
    return null;
  }

  String _defaultMimeType(ReportExportFormat format) {
    return switch (format) {
      ReportExportFormat.xlsx =>
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      ReportExportFormat.pdf => 'application/pdf',
    };
  }
}
