/// Synor Mining SDK Client for Flutter
library synor_mining;

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

export 'types.dart';

/// Synor Mining Client
class SynorMining {
  final MiningConfig config;
  final http.Client _client;
  bool _closed = false;
  StratumConnection? _activeConnection;

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

  // ==================== Pool Operations ====================

  Future<StratumConnection> connect(PoolConfig pool) async {
    final resp = await _request('POST', '/pool/connect', pool.toJson());
    _activeConnection = StratumConnection.fromJson(resp);
    return _activeConnection!;
  }

  Future<void> disconnect() async {
    if (_activeConnection == null) return;
    await _request('POST', '/pool/disconnect');
    _activeConnection = null;
  }

  Future<StratumConnection> getConnection() async {
    final resp = await _request('GET', '/pool/connection');
    _activeConnection = StratumConnection.fromJson(resp);
    return _activeConnection!;
  }

  Future<PoolStats> getPoolStats() async {
    final resp = await _request('GET', '/pool/stats');
    return PoolStats.fromJson(resp);
  }

  bool get isConnected => _activeConnection?.status == ConnectionStatus.connected;

  // ==================== Mining Operations ====================

  Future<BlockTemplate> getBlockTemplate() async {
    final resp = await _request('GET', '/mining/template');
    return BlockTemplate.fromJson(resp);
  }

  Future<SubmitResult> submitWork(MinedWork work) async {
    final resp = await _request('POST', '/mining/submit', work.toJson());
    return SubmitResult.fromJson(resp);
  }

  Future<void> startMining({String? algorithm}) async {
    final body = algorithm != null ? {'algorithm': algorithm} : null;
    await _request('POST', '/mining/start', body);
  }

  Future<void> stopMining() async {
    await _request('POST', '/mining/stop');
  }

  Future<bool> isMining() async {
    final resp = await _request('GET', '/mining/status');
    return resp['mining'] as bool;
  }

  // ==================== Stats Operations ====================

  Future<Hashrate> getHashrate() async {
    final resp = await _request('GET', '/stats/hashrate');
    return Hashrate.fromJson(resp);
  }

  Future<MiningStats> getStats() async {
    final resp = await _request('GET', '/stats');
    return MiningStats.fromJson(resp);
  }

  Future<Earnings> getEarnings({TimePeriod? period}) async {
    final path = period != null ? '/stats/earnings?period=${period.name}' : '/stats/earnings';
    final resp = await _request('GET', path);
    return Earnings.fromJson(resp);
  }

  Future<ShareStats> getShareStats() async {
    final resp = await _request('GET', '/stats/shares');
    return ShareStats.fromJson(resp);
  }

  // ==================== Device Operations ====================

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

  Future<MiningDevice> getDevice(String deviceId) async {
    final resp = await _request('GET', '/devices/${Uri.encodeComponent(deviceId)}');
    return MiningDevice.fromJson(resp);
  }

  Future<MiningDevice> setDeviceConfig(String deviceId, DeviceConfig deviceConfig) async {
    final resp = await _request('PUT', '/devices/${Uri.encodeComponent(deviceId)}/config', deviceConfig.toJson());
    return MiningDevice.fromJson(resp);
  }

  Future<void> enableDevice(String deviceId) async {
    await setDeviceConfig(deviceId, const DeviceConfig(enabled: true));
  }

  Future<void> disableDevice(String deviceId) async {
    await setDeviceConfig(deviceId, const DeviceConfig(enabled: false));
  }

  // ==================== Worker Operations ====================

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

  Future<WorkerInfo> getWorker(String workerId) async {
    final resp = await _request('GET', '/workers/${Uri.encodeComponent(workerId)}');
    return WorkerInfo.fromJson(resp);
  }

  Future<void> removeWorker(String workerId) async {
    await _request('DELETE', '/workers/${Uri.encodeComponent(workerId)}');
  }

  // ==================== Algorithm Operations ====================

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

  Future<MiningAlgorithm> getAlgorithm(String name) async {
    final resp = await _request('GET', '/algorithms/${Uri.encodeComponent(name)}');
    return MiningAlgorithm.fromJson(resp);
  }

  Future<void> switchAlgorithm(String algorithm) async {
    await _request('POST', '/algorithms/switch', {'algorithm': algorithm});
  }

  Future<MiningAlgorithm> getMostProfitable() async {
    final resp = await _request('GET', '/algorithms/profitable');
    return MiningAlgorithm.fromJson(resp);
  }

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

  void close() {
    _closed = true;
    _activeConnection = null;
    _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 MiningException('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 MiningException('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 MiningException('Unknown method: $method');
    }

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