/// Synor Mining SDK Types for Flutter
library synor_mining_types;

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

enum DeviceType { cpu, gpu_nvidia, gpu_amd, asic }
enum DeviceStatus { idle, mining, error, offline }
enum ConnectionStatus { disconnected, connecting, connected, reconnecting }
enum TimePeriod { hour, day, week, month, all }
enum SubmitResultStatus { accepted, rejected, stale }

// ==================== Pool Types ====================

class PoolConfig {
  final String url;
  final String user;
  final String? password;
  final String? algorithm;
  final double? difficulty;

  const PoolConfig({required this.url, required this.user, this.password, this.algorithm, this.difficulty});

  Map<String, dynamic> toJson() => {
        'url': url,
        'user': user,
        if (password != null) 'password': password,
        if (algorithm != null) 'algorithm': algorithm,
        if (difficulty != null) 'difficulty': difficulty,
      };
}

class StratumConnection {
  final String id;
  final String pool;
  final ConnectionStatus status;
  final String algorithm;
  final double difficulty;
  final int connectedAt;
  final int acceptedShares;
  final int rejectedShares;
  final int staleShares;
  final int? lastShareAt;

  const StratumConnection({
    required this.id,
    required this.pool,
    required this.status,
    required this.algorithm,
    required this.difficulty,
    required this.connectedAt,
    required this.acceptedShares,
    required this.rejectedShares,
    required this.staleShares,
    this.lastShareAt,
  });

  factory StratumConnection.fromJson(Map<String, dynamic> json) => StratumConnection(
        id: json['id'] as String,
        pool: json['pool'] as String,
        status: ConnectionStatus.values.firstWhere((e) => e.name == json['status']),
        algorithm: json['algorithm'] as String,
        difficulty: (json['difficulty'] as num).toDouble(),
        connectedAt: json['connectedAt'] as int,
        acceptedShares: json['acceptedShares'] as int,
        rejectedShares: json['rejectedShares'] as int,
        staleShares: json['staleShares'] as int,
        lastShareAt: json['lastShareAt'] as int?,
      );
}

class PoolStats {
  final String url;
  final int workers;
  final double hashrate;
  final double difficulty;
  final int lastBlock;
  final int blocksFound24h;
  final double luck;

  const PoolStats({
    required this.url,
    required this.workers,
    required this.hashrate,
    required this.difficulty,
    required this.lastBlock,
    required this.blocksFound24h,
    required this.luck,
  });

  factory PoolStats.fromJson(Map<String, dynamic> json) => PoolStats(
        url: json['url'] as String,
        workers: json['workers'] as int,
        hashrate: (json['hashrate'] as num).toDouble(),
        difficulty: (json['difficulty'] as num).toDouble(),
        lastBlock: json['lastBlock'] as int,
        blocksFound24h: json['blocksFound24h'] as int,
        luck: (json['luck'] as num).toDouble(),
      );
}

// ==================== Mining Types ====================

class TemplateTransaction {
  final String txid;
  final String data;
  final String fee;
  final int weight;

  const TemplateTransaction({required this.txid, required this.data, required this.fee, required this.weight});

  factory TemplateTransaction.fromJson(Map<String, dynamic> json) => TemplateTransaction(
        txid: json['txid'] as String,
        data: json['data'] as String,
        fee: json['fee'] as String,
        weight: json['weight'] as int,
      );
}

class BlockTemplate {
  final String id;
  final String previousBlockHash;
  final String merkleRoot;
  final int timestamp;
  final String bits;
  final int height;
  final String coinbaseValue;
  final List<TemplateTransaction> transactions;
  final String target;
  final String algorithm;
  final String extraNonce;

  const BlockTemplate({
    required this.id,
    required this.previousBlockHash,
    required this.merkleRoot,
    required this.timestamp,
    required this.bits,
    required this.height,
    required this.coinbaseValue,
    required this.transactions,
    required this.target,
    required this.algorithm,
    required this.extraNonce,
  });

  factory BlockTemplate.fromJson(Map<String, dynamic> json) => BlockTemplate(
        id: json['id'] as String,
        previousBlockHash: json['previousBlockHash'] as String,
        merkleRoot: json['merkleRoot'] as String,
        timestamp: json['timestamp'] as int,
        bits: json['bits'] as String,
        height: json['height'] as int,
        coinbaseValue: json['coinbaseValue'] as String,
        transactions: (json['transactions'] as List).map((e) => TemplateTransaction.fromJson(e)).toList(),
        target: json['target'] as String,
        algorithm: json['algorithm'] as String,
        extraNonce: json['extraNonce'] as String,
      );
}

class MinedWork {
  final String templateId;
  final String nonce;
  final String extraNonce;
  final int timestamp;
  final String hash;

  const MinedWork({
    required this.templateId,
    required this.nonce,
    required this.extraNonce,
    required this.timestamp,
    required this.hash,
  });

  Map<String, dynamic> toJson() => {
        'templateId': templateId,
        'nonce': nonce,
        'extraNonce': extraNonce,
        'timestamp': timestamp,
        'hash': hash,
      };
}

class ShareInfo {
  final String hash;
  final double difficulty;
  final int timestamp;
  final bool accepted;

  const ShareInfo({required this.hash, required this.difficulty, required this.timestamp, required this.accepted});

  factory ShareInfo.fromJson(Map<String, dynamic> json) => ShareInfo(
        hash: json['hash'] as String,
        difficulty: (json['difficulty'] as num).toDouble(),
        timestamp: json['timestamp'] as int,
        accepted: json['accepted'] as bool,
      );
}

class SubmitResult {
  final SubmitResultStatus status;
  final ShareInfo share;
  final bool blockFound;
  final String? reason;
  final String? blockHash;
  final String? reward;

  const SubmitResult({
    required this.status,
    required this.share,
    required this.blockFound,
    this.reason,
    this.blockHash,
    this.reward,
  });

  factory SubmitResult.fromJson(Map<String, dynamic> json) => SubmitResult(
        status: SubmitResultStatus.values.firstWhere((e) => e.name == json['status']),
        share: ShareInfo.fromJson(json['share']),
        blockFound: json['blockFound'] as bool,
        reason: json['reason'] as String?,
        blockHash: json['blockHash'] as String?,
        reward: json['reward'] as String?,
      );
}

// ==================== Stats Types ====================

class Hashrate {
  final double current;
  final double average1h;
  final double average24h;
  final double peak;
  final String unit;

  const Hashrate({required this.current, required this.average1h, required this.average24h, required this.peak, required this.unit});

  factory Hashrate.fromJson(Map<String, dynamic> json) => Hashrate(
        current: (json['current'] as num).toDouble(),
        average1h: (json['average1h'] as num).toDouble(),
        average24h: (json['average24h'] as num).toDouble(),
        peak: (json['peak'] as num).toDouble(),
        unit: json['unit'] as String,
      );
}

class ShareStats {
  final int accepted;
  final int rejected;
  final int stale;
  final int total;
  final double acceptRate;

  const ShareStats({required this.accepted, required this.rejected, required this.stale, required this.total, required this.acceptRate});

  factory ShareStats.fromJson(Map<String, dynamic> json) => ShareStats(
        accepted: json['accepted'] as int,
        rejected: json['rejected'] as int,
        stale: json['stale'] as int,
        total: json['total'] as int,
        acceptRate: (json['acceptRate'] as num).toDouble(),
      );
}

class DeviceTemperature {
  final double current;
  final double max;
  final bool throttling;

  const DeviceTemperature({required this.current, required this.max, required this.throttling});

  factory DeviceTemperature.fromJson(Map<String, dynamic> json) => DeviceTemperature(
        current: (json['current'] as num).toDouble(),
        max: (json['max'] as num).toDouble(),
        throttling: json['throttling'] as bool,
      );
}

class EarningsSnapshot {
  final String today;
  final String yesterday;
  final String thisWeek;
  final String thisMonth;
  final String total;
  final String currency;

  const EarningsSnapshot({
    required this.today,
    required this.yesterday,
    required this.thisWeek,
    required this.thisMonth,
    required this.total,
    required this.currency,
  });

  factory EarningsSnapshot.fromJson(Map<String, dynamic> json) => EarningsSnapshot(
        today: json['today'] as String,
        yesterday: json['yesterday'] as String,
        thisWeek: json['thisWeek'] as String,
        thisMonth: json['thisMonth'] as String,
        total: json['total'] as String,
        currency: json['currency'] as String,
      );
}

class MiningStats {
  final Hashrate hashrate;
  final ShareStats shares;
  final int uptime;
  final double efficiency;
  final EarningsSnapshot earnings;
  final double? powerConsumption;
  final DeviceTemperature? temperature;

  const MiningStats({
    required this.hashrate,
    required this.shares,
    required this.uptime,
    required this.efficiency,
    required this.earnings,
    this.powerConsumption,
    this.temperature,
  });

  factory MiningStats.fromJson(Map<String, dynamic> json) => MiningStats(
        hashrate: Hashrate.fromJson(json['hashrate']),
        shares: ShareStats.fromJson(json['shares']),
        uptime: json['uptime'] as int,
        efficiency: (json['efficiency'] as num).toDouble(),
        earnings: EarningsSnapshot.fromJson(json['earnings']),
        powerConsumption: (json['powerConsumption'] as num?)?.toDouble(),
        temperature: json['temperature'] != null ? DeviceTemperature.fromJson(json['temperature']) : null,
      );
}

class EarningsBreakdown {
  final int date;
  final String amount;
  final int blocks;
  final int shares;
  final double hashrate;

  const EarningsBreakdown({required this.date, required this.amount, required this.blocks, required this.shares, required this.hashrate});

  factory EarningsBreakdown.fromJson(Map<String, dynamic> json) => EarningsBreakdown(
        date: json['date'] as int,
        amount: json['amount'] as String,
        blocks: json['blocks'] as int,
        shares: json['shares'] as int,
        hashrate: (json['hashrate'] as num).toDouble(),
      );
}

class Earnings {
  final TimePeriod period;
  final int startDate;
  final int endDate;
  final String amount;
  final int blocks;
  final int shares;
  final double averageHashrate;
  final String currency;
  final List<EarningsBreakdown> breakdown;

  const Earnings({
    required this.period,
    required this.startDate,
    required this.endDate,
    required this.amount,
    required this.blocks,
    required this.shares,
    required this.averageHashrate,
    required this.currency,
    required this.breakdown,
  });

  factory Earnings.fromJson(Map<String, dynamic> json) => Earnings(
        period: TimePeriod.values.firstWhere((e) => e.name == json['period']),
        startDate: json['startDate'] as int,
        endDate: json['endDate'] as int,
        amount: json['amount'] as String,
        blocks: json['blocks'] as int,
        shares: json['shares'] as int,
        averageHashrate: (json['averageHashrate'] as num).toDouble(),
        currency: json['currency'] as String,
        breakdown: (json['breakdown'] as List).map((e) => EarningsBreakdown.fromJson(e)).toList(),
      );
}

// ==================== Device Types ====================

class MiningDevice {
  final String id;
  final String name;
  final DeviceType type;
  final DeviceStatus status;
  final double hashrate;
  final double temperature;
  final double fanSpeed;
  final double powerDraw;
  final int memoryUsed;
  final int memoryTotal;
  final String? driver;
  final String? firmware;

  const MiningDevice({
    required this.id,
    required this.name,
    required this.type,
    required this.status,
    required this.hashrate,
    required this.temperature,
    required this.fanSpeed,
    required this.powerDraw,
    required this.memoryUsed,
    required this.memoryTotal,
    this.driver,
    this.firmware,
  });

  factory MiningDevice.fromJson(Map<String, dynamic> json) => MiningDevice(
        id: json['id'] as String,
        name: json['name'] as String,
        type: DeviceType.values.firstWhere((e) => e.name == json['type']),
        status: DeviceStatus.values.firstWhere((e) => e.name == json['status']),
        hashrate: (json['hashrate'] as num).toDouble(),
        temperature: (json['temperature'] as num).toDouble(),
        fanSpeed: (json['fanSpeed'] as num).toDouble(),
        powerDraw: (json['powerDraw'] as num).toDouble(),
        memoryUsed: json['memoryUsed'] as int,
        memoryTotal: json['memoryTotal'] as int,
        driver: json['driver'] as String?,
        firmware: json['firmware'] as String?,
      );
}

class DeviceConfig {
  final bool enabled;
  final int? intensity;
  final int? powerLimit;
  final int? coreClockOffset;
  final int? memoryClockOffset;
  final int? fanSpeed;

  const DeviceConfig({
    required this.enabled,
    this.intensity,
    this.powerLimit,
    this.coreClockOffset,
    this.memoryClockOffset,
    this.fanSpeed,
  });

  Map<String, dynamic> toJson() => {
        'enabled': enabled,
        if (intensity != null) 'intensity': intensity,
        if (powerLimit != null) 'powerLimit': powerLimit,
        if (coreClockOffset != null) 'coreClockOffset': coreClockOffset,
        if (memoryClockOffset != null) 'memoryClockOffset': memoryClockOffset,
        if (fanSpeed != null) 'fanSpeed': fanSpeed,
      };
}

class WorkerInfo {
  final String id;
  final String name;
  final ConnectionStatus status;
  final Hashrate hashrate;
  final ShareStats shares;
  final List<MiningDevice> devices;
  final int lastSeen;
  final int uptime;

  const WorkerInfo({
    required this.id,
    required this.name,
    required this.status,
    required this.hashrate,
    required this.shares,
    required this.devices,
    required this.lastSeen,
    required this.uptime,
  });

  factory WorkerInfo.fromJson(Map<String, dynamic> json) => WorkerInfo(
        id: json['id'] as String,
        name: json['name'] as String,
        status: ConnectionStatus.values.firstWhere((e) => e.name == json['status']),
        hashrate: Hashrate.fromJson(json['hashrate']),
        shares: ShareStats.fromJson(json['shares']),
        devices: (json['devices'] as List).map((e) => MiningDevice.fromJson(e)).toList(),
        lastSeen: json['lastSeen'] as int,
        uptime: json['uptime'] as int,
      );
}

class MiningAlgorithm {
  final String name;
  final String displayName;
  final String hashUnit;
  final String profitability;
  final double difficulty;
  final String blockReward;
  final int blockTime;

  const MiningAlgorithm({
    required this.name,
    required this.displayName,
    required this.hashUnit,
    required this.profitability,
    required this.difficulty,
    required this.blockReward,
    required this.blockTime,
  });

  factory MiningAlgorithm.fromJson(Map<String, dynamic> json) => MiningAlgorithm(
        name: json['name'] as String,
        displayName: json['displayName'] as String,
        hashUnit: json['hashUnit'] as String,
        profitability: json['profitability'] as String,
        difficulty: (json['difficulty'] as num).toDouble(),
        blockReward: json['blockReward'] as String,
        blockTime: json['blockTime'] as int,
      );
}

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

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

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

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

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

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