/// Synor Economics SDK Client for Flutter
library synor_economics;

import 'dart:convert';
import 'dart:typed_data';
import 'package:http/http.dart' as http;
import 'types.dart';

export 'types.dart';

/// Synor Economics Client
class SynorEconomics {
  final EconomicsConfig config;
  final http.Client _client;
  bool _closed = false;

  SynorEconomics(this.config) : _client = http.Client();

  // ==================== Pricing Operations ====================

  Future<Price> getPrice(ServiceType service, UsageMetrics usage) async {
    final resp = await _request('POST', '/pricing/calculate', {
      'service': service.name,
      'usage': usage.toJson(),
    });
    return Price.fromJson(resp);
  }

  Future<CostEstimate> estimateCost(UsagePlan plan) async {
    final resp = await _request('POST', '/pricing/estimate', plan.toJson());
    return CostEstimate.fromJson(resp);
  }

  Future<List<PricingTier>> getPricingTiers(ServiceType service) async {
    final resp = await _request('GET', '/pricing/tiers/${service.name}');
    return (resp['tiers'] as List).map((e) => PricingTier.fromJson(e)).toList();
  }

  // ==================== Billing Operations ====================

  Future<Usage> getUsage({BillingPeriod? period}) async {
    final path = period != null ? '/billing/usage?period=${period.name}' : '/billing/usage';
    final resp = await _request('GET', path);
    return Usage.fromJson(resp);
  }

  Future<Usage> getUsageByDateRange(int startDate, int endDate) async {
    final resp = await _request('GET', '/billing/usage?startDate=$startDate&endDate=$endDate');
    return Usage.fromJson(resp);
  }

  Future<List<Invoice>> getInvoices() async {
    final resp = await _request('GET', '/billing/invoices');
    return (resp['invoices'] as List).map((e) => Invoice.fromJson(e)).toList();
  }

  Future<Invoice> getInvoice(String invoiceId) async {
    final resp = await _request('GET', '/billing/invoices/${Uri.encodeComponent(invoiceId)}');
    return Invoice.fromJson(resp);
  }

  Future<Uint8List> downloadInvoice(String invoiceId) async {
    final resp = await _request('GET', '/billing/invoices/${Uri.encodeComponent(invoiceId)}/pdf');
    return base64Decode(resp['data'] as String);
  }

  Future<AccountBalance> getBalance() async {
    final resp = await _request('GET', '/billing/balance');
    return AccountBalance.fromJson(resp);
  }

  Future<void> addCredits(String amount, String paymentMethod) async {
    await _request('POST', '/billing/credits', {
      'amount': amount,
      'paymentMethod': paymentMethod,
    });
  }

  // ==================== Staking Operations ====================

  Future<StakeReceipt> stake(String amount, {int? durationDays}) async {
    final body = <String, dynamic>{'amount': amount};
    if (durationDays != null) body['durationDays'] = durationDays;
    final resp = await _request('POST', '/staking/stake', body);
    return StakeReceipt.fromJson(resp);
  }

  Future<UnstakeReceipt> unstake(String stakeId) async {
    final resp = await _request('POST', '/staking/unstake/${Uri.encodeComponent(stakeId)}');
    return UnstakeReceipt.fromJson(resp);
  }

  Future<List<StakeInfo>> getStakes() async {
    final resp = await _request('GET', '/staking/stakes');
    return (resp['stakes'] as List).map((e) => StakeInfo.fromJson(e)).toList();
  }

  Future<StakeInfo> getStake(String stakeId) async {
    final resp = await _request('GET', '/staking/stakes/${Uri.encodeComponent(stakeId)}');
    return StakeInfo.fromJson(resp);
  }

  Future<Rewards> getStakingRewards() async {
    final resp = await _request('GET', '/staking/rewards');
    return Rewards.fromJson(resp);
  }

  Future<String> claimRewards() async {
    final resp = await _request('POST', '/staking/rewards/claim');
    return resp['txHash'] as String;
  }

  Future<double> getCurrentApy() async {
    final resp = await _request('GET', '/staking/apy');
    return (resp['apy'] as num).toDouble();
  }

  // ==================== Discount Operations ====================

  Future<AppliedDiscount> applyDiscount(String code) async {
    final resp = await _request('POST', '/discounts/apply', {'code': code});
    return AppliedDiscount.fromJson(resp);
  }

  Future<List<Discount>> getAvailableDiscounts() async {
    final resp = await _request('GET', '/discounts/available');
    return (resp['discounts'] as List).map((e) => Discount.fromJson(e)).toList();
  }

  Future<Discount> validateDiscount(String code) async {
    final resp = await _request('GET', '/discounts/validate/${Uri.encodeComponent(code)}');
    return Discount.fromJson(resp);
  }

  Future<List<AppliedDiscount>> getAppliedDiscounts() async {
    final resp = await _request('GET', '/discounts/applied');
    return (resp['discounts'] as List).map((e) => AppliedDiscount.fromJson(e)).toList();
  }

  Future<void> removeDiscount(String discountId) async {
    await _request('DELETE', '/discounts/${Uri.encodeComponent(discountId)}');
  }

  // ==================== Lifecycle ====================

  void close() {
    _closed = true;
    _client.close();
  }

  bool get isClosed => _closed;

  Future<bool> healthCheck() async {
    try {
      final resp = await _request('GET', '/health');
      return resp['status'] == 'healthy';
    } catch (_) {
      return false;
    }
  }

  // ==================== Private Methods ====================

  Future<Map<String, dynamic>> _request(String method, String path, [Map<String, dynamic>? body]) async {
    if (_closed) throw const EconomicsException('Client has been closed');

    Exception? lastError;
    for (var attempt = 0; attempt < config.retries; attempt++) {
      try {
        return await _doRequest(method, path, body);
      } catch (e) {
        lastError = e as Exception;
        if (attempt < config.retries - 1) {
          await Future.delayed(Duration(seconds: 1 << attempt));
        }
      }
    }
    throw lastError ?? const EconomicsException('Unknown error');
  }

  Future<Map<String, dynamic>> _doRequest(String method, String path, Map<String, dynamic>? body) async {
    final uri = Uri.parse('${config.endpoint}$path');
    final headers = {
      'Authorization': 'Bearer ${config.apiKey}',
      'Content-Type': 'application/json',
      'X-SDK-Version': 'flutter/0.1.0',
    };

    http.Response response;
    switch (method) {
      case 'GET':
        response = await _client.get(uri, headers: headers).timeout(config.timeout);
        break;
      case 'POST':
        response = await _client.post(uri, headers: headers, body: body != null ? jsonEncode(body) : null).timeout(config.timeout);
        break;
      case 'PUT':
        response = await _client.put(uri, headers: headers, body: body != null ? jsonEncode(body) : null).timeout(config.timeout);
        break;
      case 'DELETE':
        response = await _client.delete(uri, headers: headers).timeout(config.timeout);
        break;
      default:
        throw EconomicsException('Unknown method: $method');
    }

    final respBody = jsonDecode(response.body) as Map<String, dynamic>;
    if (response.statusCode >= 400) {
      throw EconomicsException(
        respBody['message'] as String? ?? 'HTTP ${response.statusCode}',
        code: respBody['code'] as String?,
        statusCode: response.statusCode,
      );
    }
    return respBody;
  }
}
