/// Synor Economics SDK Types for Flutter
library synor_economics_types;

// ==================== Enums ====================

enum ServiceType { compute, storage, database, hosting, bridge, mining, rpc }
enum BillingPeriod { daily, weekly, monthly, yearly }
enum StakeStatus { active, unstaking, unlocked, slashed }
enum DiscountType { percentage, fixed, volume }

// ==================== Pricing Types ====================

class UsageMetrics {
  final double computeHours;
  final double storageGb;
  final int requests;
  final double bandwidthGb;
  final double gpuHours;

  const UsageMetrics({
    this.computeHours = 0,
    this.storageGb = 0,
    this.requests = 0,
    this.bandwidthGb = 0,
    this.gpuHours = 0,
  });

  Map<String, dynamic> toJson() => {
        'computeHours': computeHours,
        'storageGb': storageGb,
        'requests': requests,
        'bandwidthGb': bandwidthGb,
        'gpuHours': gpuHours,
      };
}

class PricingTier {
  final String name;
  final double upTo;
  final double pricePerUnit;
  final String unit;

  const PricingTier({
    required this.name,
    required this.upTo,
    required this.pricePerUnit,
    required this.unit,
  });

  factory PricingTier.fromJson(Map<String, dynamic> json) => PricingTier(
        name: json['name'] as String,
        upTo: (json['upTo'] as num).toDouble(),
        pricePerUnit: (json['pricePerUnit'] as num).toDouble(),
        unit: json['unit'] as String,
      );
}

class Price {
  final ServiceType service;
  final String amount;
  final String currency;
  final String amountUsd;
  final List<PricingTier>? breakdown;
  final String? discount;
  final String finalAmount;

  const Price({
    required this.service,
    required this.amount,
    required this.currency,
    required this.amountUsd,
    this.breakdown,
    this.discount,
    required this.finalAmount,
  });

  factory Price.fromJson(Map<String, dynamic> json) => Price(
        service: ServiceType.values.firstWhere((e) => e.name == json['service']),
        amount: json['amount'] as String,
        currency: json['currency'] as String,
        amountUsd: json['amountUsd'] as String,
        breakdown: (json['breakdown'] as List?)?.map((e) => PricingTier.fromJson(e)).toList(),
        discount: json['discount'] as String?,
        finalAmount: json['finalAmount'] as String,
      );
}

class UsagePlan {
  final ServiceType service;
  final UsageMetrics estimatedUsage;
  final BillingPeriod period;

  const UsagePlan({required this.service, required this.estimatedUsage, required this.period});

  Map<String, dynamic> toJson() => {
        'service': service.name,
        'estimatedUsage': estimatedUsage.toJson(),
        'period': period.name,
      };
}

class CostBreakdown {
  final String category;
  final String amount;
  final double percentage;

  const CostBreakdown({required this.category, required this.amount, required this.percentage});

  factory CostBreakdown.fromJson(Map<String, dynamic> json) => CostBreakdown(
        category: json['category'] as String,
        amount: json['amount'] as String,
        percentage: (json['percentage'] as num).toDouble(),
      );
}

class CostEstimate {
  final ServiceType service;
  final BillingPeriod period;
  final String estimatedCost;
  final String estimatedCostUsd;
  final String currency;
  final String confidenceLevel;
  final List<CostBreakdown>? breakdown;
  final String? savingsFromStaking;

  const CostEstimate({
    required this.service,
    required this.period,
    required this.estimatedCost,
    required this.estimatedCostUsd,
    required this.currency,
    required this.confidenceLevel,
    this.breakdown,
    this.savingsFromStaking,
  });

  factory CostEstimate.fromJson(Map<String, dynamic> json) => CostEstimate(
        service: ServiceType.values.firstWhere((e) => e.name == json['service']),
        period: BillingPeriod.values.firstWhere((e) => e.name == json['period']),
        estimatedCost: json['estimatedCost'] as String,
        estimatedCostUsd: json['estimatedCostUsd'] as String,
        currency: json['currency'] as String,
        confidenceLevel: json['confidenceLevel'] as String,
        breakdown: (json['breakdown'] as List?)?.map((e) => CostBreakdown.fromJson(e)).toList(),
        savingsFromStaking: json['savingsFromStaking'] as String?,
      );
}

// ==================== Billing Types ====================

class UsageRecord {
  final ServiceType service;
  final String resource;
  final double quantity;
  final String unit;
  final int timestamp;
  final String cost;

  const UsageRecord({
    required this.service,
    required this.resource,
    required this.quantity,
    required this.unit,
    required this.timestamp,
    required this.cost,
  });

  factory UsageRecord.fromJson(Map<String, dynamic> json) => UsageRecord(
        service: ServiceType.values.firstWhere((e) => e.name == json['service']),
        resource: json['resource'] as String,
        quantity: (json['quantity'] as num).toDouble(),
        unit: json['unit'] as String,
        timestamp: json['timestamp'] as int,
        cost: json['cost'] as String,
      );
}

class Usage {
  final BillingPeriod period;
  final int startDate;
  final int endDate;
  final List<UsageRecord> records;
  final String totalCost;
  final String currency;

  const Usage({
    required this.period,
    required this.startDate,
    required this.endDate,
    required this.records,
    required this.totalCost,
    required this.currency,
  });

  factory Usage.fromJson(Map<String, dynamic> json) => Usage(
        period: BillingPeriod.values.firstWhere((e) => e.name == json['period']),
        startDate: json['startDate'] as int,
        endDate: json['endDate'] as int,
        records: (json['records'] as List).map((e) => UsageRecord.fromJson(e)).toList(),
        totalCost: json['totalCost'] as String,
        currency: json['currency'] as String,
      );
}

class InvoiceLineItem {
  final String description;
  final double quantity;
  final String unit;
  final String unitPrice;
  final String amount;

  const InvoiceLineItem({
    required this.description,
    required this.quantity,
    required this.unit,
    required this.unitPrice,
    required this.amount,
  });

  factory InvoiceLineItem.fromJson(Map<String, dynamic> json) => InvoiceLineItem(
        description: json['description'] as String,
        quantity: (json['quantity'] as num).toDouble(),
        unit: json['unit'] as String,
        unitPrice: json['unitPrice'] as String,
        amount: json['amount'] as String,
      );
}

class Invoice {
  final String id;
  final String number;
  final int periodStart;
  final int periodEnd;
  final String subtotal;
  final String tax;
  final String discount;
  final String total;
  final String currency;
  final String status;
  final int? dueDate;
  final int? paidAt;
  final List<InvoiceLineItem> lineItems;
  final String? pdfUrl;

  const Invoice({
    required this.id,
    required this.number,
    required this.periodStart,
    required this.periodEnd,
    required this.subtotal,
    required this.tax,
    required this.discount,
    required this.total,
    required this.currency,
    required this.status,
    this.dueDate,
    this.paidAt,
    required this.lineItems,
    this.pdfUrl,
  });

  factory Invoice.fromJson(Map<String, dynamic> json) => Invoice(
        id: json['id'] as String,
        number: json['number'] as String,
        periodStart: json['periodStart'] as int,
        periodEnd: json['periodEnd'] as int,
        subtotal: json['subtotal'] as String,
        tax: json['tax'] as String,
        discount: json['discount'] as String,
        total: json['total'] as String,
        currency: json['currency'] as String,
        status: json['status'] as String,
        dueDate: json['dueDate'] as int?,
        paidAt: json['paidAt'] as int?,
        lineItems: (json['lineItems'] as List).map((e) => InvoiceLineItem.fromJson(e)).toList(),
        pdfUrl: json['pdfUrl'] as String?,
      );
}

class AccountBalance {
  final String available;
  final String pending;
  final String reserved;
  final String staked;
  final String currency;
  final int lastUpdated;

  const AccountBalance({
    required this.available,
    required this.pending,
    required this.reserved,
    required this.staked,
    required this.currency,
    required this.lastUpdated,
  });

  factory AccountBalance.fromJson(Map<String, dynamic> json) => AccountBalance(
        available: json['available'] as String,
        pending: json['pending'] as String,
        reserved: json['reserved'] as String,
        staked: json['staked'] as String,
        currency: json['currency'] as String,
        lastUpdated: json['lastUpdated'] as int,
      );
}

// ==================== Staking Types ====================

class StakeReceipt {
  final String id;
  final String txHash;
  final String amount;
  final int startDate;
  final int endDate;
  final double apy;
  final StakeStatus status;

  const StakeReceipt({
    required this.id,
    required this.txHash,
    required this.amount,
    required this.startDate,
    required this.endDate,
    required this.apy,
    required this.status,
  });

  factory StakeReceipt.fromJson(Map<String, dynamic> json) => StakeReceipt(
        id: json['id'] as String,
        txHash: json['txHash'] as String,
        amount: json['amount'] as String,
        startDate: json['startDate'] as int,
        endDate: json['endDate'] as int,
        apy: (json['apy'] as num).toDouble(),
        status: StakeStatus.values.firstWhere((e) => e.name == json['status']),
      );
}

class StakeInfo {
  final String id;
  final String amount;
  final StakeStatus status;
  final int startDate;
  final int endDate;
  final double apy;
  final String earnedRewards;
  final String pendingRewards;
  final int? unlockDate;
  final int discountPercent;

  const StakeInfo({
    required this.id,
    required this.amount,
    required this.status,
    required this.startDate,
    required this.endDate,
    required this.apy,
    required this.earnedRewards,
    required this.pendingRewards,
    this.unlockDate,
    this.discountPercent = 0,
  });

  factory StakeInfo.fromJson(Map<String, dynamic> json) => StakeInfo(
        id: json['id'] as String,
        amount: json['amount'] as String,
        status: StakeStatus.values.firstWhere((e) => e.name == json['status']),
        startDate: json['startDate'] as int,
        endDate: json['endDate'] as int,
        apy: (json['apy'] as num).toDouble(),
        earnedRewards: json['earnedRewards'] as String,
        pendingRewards: json['pendingRewards'] as String,
        unlockDate: json['unlockDate'] as int?,
        discountPercent: json['discountPercent'] as int? ?? 0,
      );
}

class UnstakeReceipt {
  final String id;
  final String txHash;
  final String amount;
  final int initiatedAt;
  final int availableAt;
  final String? penalty;

  const UnstakeReceipt({
    required this.id,
    required this.txHash,
    required this.amount,
    required this.initiatedAt,
    required this.availableAt,
    this.penalty,
  });

  factory UnstakeReceipt.fromJson(Map<String, dynamic> json) => UnstakeReceipt(
        id: json['id'] as String,
        txHash: json['txHash'] as String,
        amount: json['amount'] as String,
        initiatedAt: json['initiatedAt'] as int,
        availableAt: json['availableAt'] as int,
        penalty: json['penalty'] as String?,
      );
}

class RewardRecord {
  final int date;
  final String amount;
  final String type;
  final String? txHash;

  const RewardRecord({required this.date, required this.amount, required this.type, this.txHash});

  factory RewardRecord.fromJson(Map<String, dynamic> json) => RewardRecord(
        date: json['date'] as int,
        amount: json['amount'] as String,
        type: json['type'] as String,
        txHash: json['txHash'] as String?,
      );
}

class Rewards {
  final String totalEarned;
  final String pendingClaim;
  final String? lastClaimDate;
  final double currentApy;
  final List<RewardRecord> history;

  const Rewards({
    required this.totalEarned,
    required this.pendingClaim,
    this.lastClaimDate,
    required this.currentApy,
    required this.history,
  });

  factory Rewards.fromJson(Map<String, dynamic> json) => Rewards(
        totalEarned: json['totalEarned'] as String,
        pendingClaim: json['pendingClaim'] as String,
        lastClaimDate: json['lastClaimDate'] as String?,
        currentApy: (json['currentApy'] as num).toDouble(),
        history: (json['history'] as List).map((e) => RewardRecord.fromJson(e)).toList(),
      );
}

// ==================== Discount Types ====================

class Discount {
  final String id;
  final String code;
  final DiscountType type;
  final String value;
  final int? validFrom;
  final int? validUntil;
  final int? usageLimit;
  final int usageCount;
  final List<ServiceType>? applicableServices;
  final String? minimumSpend;
  final bool active;

  const Discount({
    required this.id,
    required this.code,
    required this.type,
    required this.value,
    this.validFrom,
    this.validUntil,
    this.usageLimit,
    this.usageCount = 0,
    this.applicableServices,
    this.minimumSpend,
    this.active = true,
  });

  factory Discount.fromJson(Map<String, dynamic> json) => Discount(
        id: json['id'] as String,
        code: json['code'] as String,
        type: DiscountType.values.firstWhere((e) => e.name == json['type']),
        value: json['value'] as String,
        validFrom: json['validFrom'] as int?,
        validUntil: json['validUntil'] as int?,
        usageLimit: json['usageLimit'] as int?,
        usageCount: json['usageCount'] as int? ?? 0,
        applicableServices: (json['applicableServices'] as List?)
            ?.map((e) => ServiceType.values.firstWhere((s) => s.name == e))
            .toList(),
        minimumSpend: json['minimumSpend'] as String?,
        active: json['active'] as bool? ?? true,
      );
}

class AppliedDiscount {
  final Discount discount;
  final String savedAmount;
  final int appliedAt;

  const AppliedDiscount({required this.discount, required this.savedAmount, required this.appliedAt});

  factory AppliedDiscount.fromJson(Map<String, dynamic> json) => AppliedDiscount(
        discount: Discount.fromJson(json['discount']),
        savedAmount: json['savedAmount'] as String,
        appliedAt: json['appliedAt'] as int,
      );
}

// ==================== Config & Error ====================

class EconomicsConfig {
  final String apiKey;
  final String endpoint;
  final Duration timeout;
  final int retries;
  final bool debug;

  const EconomicsConfig({
    required this.apiKey,
    this.endpoint = 'https://economics.synor.io/v1',
    this.timeout = const Duration(seconds: 60),
    this.retries = 3,
    this.debug = false,
  });
}

class EconomicsException implements Exception {
  final String message;
  final String? code;
  final int statusCode;

  const EconomicsException(this.message, {this.code, this.statusCode = 0});

  @override
  String toString() => 'EconomicsException: $message';
}
