/// Synor Governance SDK Client for Flutter
library synor_governance;

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

export 'types.dart';

/// Synor Governance Client
class SynorGovernance {
  final GovernanceConfig config;
  final http.Client _client;
  bool _closed = false;

  static const _finalStatuses = {
    ProposalStatus.passed,
    ProposalStatus.rejected,
    ProposalStatus.executed,
    ProposalStatus.cancelled,
  };

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

  // ==================== Proposal Operations ====================

  Future<Proposal> createProposal(ProposalDraft proposal) async {
    final resp = await _request('POST', '/proposals', proposal.toJson());
    return Proposal.fromJson(resp);
  }

  Future<Proposal> getProposal(String proposalId) async {
    final resp = await _request('GET', '/proposals/${Uri.encodeComponent(proposalId)}');
    return Proposal.fromJson(resp);
  }

  Future<List<Proposal>> listProposals({ProposalFilter? filter}) async {
    final params = <String>[];
    if (filter?.status != null) params.add('status=${filter!.status!.name}');
    if (filter?.proposer != null) params.add('proposer=${Uri.encodeComponent(filter!.proposer!)}');
    if (filter?.daoId != null) params.add('daoId=${Uri.encodeComponent(filter!.daoId!)}');
    if (filter?.limit != null) params.add('limit=${filter!.limit}');
    if (filter?.offset != null) params.add('offset=${filter!.offset}');

    final path = params.isEmpty ? '/proposals' : '/proposals?${params.join('&')}';
    final resp = await _request('GET', path);
    return (resp['proposals'] as List).map((e) => Proposal.fromJson(e)).toList();
  }

  Future<Proposal> cancelProposal(String proposalId) async {
    final resp = await _request('POST', '/proposals/${Uri.encodeComponent(proposalId)}/cancel');
    return Proposal.fromJson(resp);
  }

  Future<Proposal> executeProposal(String proposalId) async {
    final resp = await _request('POST', '/proposals/${Uri.encodeComponent(proposalId)}/execute');
    return Proposal.fromJson(resp);
  }

  Future<Proposal> waitForProposal(
    String proposalId, {
    Duration pollInterval = const Duration(minutes: 1),
    Duration maxWait = const Duration(days: 7),
  }) async {
    final deadline = DateTime.now().add(maxWait);
    while (DateTime.now().isBefore(deadline)) {
      final proposal = await getProposal(proposalId);
      if (_finalStatuses.contains(proposal.status)) return proposal;
      await Future.delayed(pollInterval);
    }
    throw const GovernanceException('Timeout waiting for proposal completion');
  }

  // ==================== Voting Operations ====================

  Future<VoteReceipt> vote(String proposalId, Vote vote, {String? weight}) async {
    final body = <String, dynamic>{'choice': vote.choice.name};
    if (vote.reason != null) body['reason'] = vote.reason;
    if (weight != null) body['weight'] = weight;
    final resp = await _request('POST', '/proposals/${Uri.encodeComponent(proposalId)}/vote', body);
    return VoteReceipt.fromJson(resp);
  }

  Future<List<VoteReceipt>> getVotes(String proposalId) async {
    final resp = await _request('GET', '/proposals/${Uri.encodeComponent(proposalId)}/votes');
    return (resp['votes'] as List).map((e) => VoteReceipt.fromJson(e)).toList();
  }

  Future<VoteReceipt> getMyVote(String proposalId) async {
    final resp = await _request('GET', '/proposals/${Uri.encodeComponent(proposalId)}/votes/me');
    return VoteReceipt.fromJson(resp);
  }

  Future<DelegationReceipt> delegate(String delegatee, {String? amount}) async {
    final body = <String, dynamic>{'delegatee': delegatee};
    if (amount != null) body['amount'] = amount;
    final resp = await _request('POST', '/voting/delegate', body);
    return DelegationReceipt.fromJson(resp);
  }

  Future<DelegationReceipt> undelegate(String delegatee) async {
    final resp = await _request('POST', '/voting/undelegate', {'delegatee': delegatee});
    return DelegationReceipt.fromJson(resp);
  }

  Future<VotingPower> getVotingPower(String address) async {
    final resp = await _request('GET', '/voting/power/${Uri.encodeComponent(address)}');
    return VotingPower.fromJson(resp);
  }

  Future<List<DelegationReceipt>> getDelegations(String address) async {
    final resp = await _request('GET', '/voting/delegations/${Uri.encodeComponent(address)}');
    return (resp['delegations'] as List).map((e) => DelegationReceipt.fromJson(e)).toList();
  }

  // ==================== DAO Operations ====================

  Future<Dao> createDao(DaoConfig daoConfig) async {
    final resp = await _request('POST', '/daos', daoConfig.toJson());
    return Dao.fromJson(resp);
  }

  Future<Dao> getDao(String daoId) async {
    final resp = await _request('GET', '/daos/${Uri.encodeComponent(daoId)}');
    return Dao.fromJson(resp);
  }

  Future<List<Dao>> listDaos({int? limit, int? offset}) async {
    final params = <String>[];
    if (limit != null) params.add('limit=$limit');
    if (offset != null) params.add('offset=$offset');
    final path = params.isEmpty ? '/daos' : '/daos?${params.join('&')}';
    final resp = await _request('GET', path);
    return (resp['daos'] as List).map((e) => Dao.fromJson(e)).toList();
  }

  Future<DaoTreasury> getDaoTreasury(String daoId) async {
    final resp = await _request('GET', '/daos/${Uri.encodeComponent(daoId)}/treasury');
    return DaoTreasury.fromJson(resp);
  }

  Future<List<String>> getDaoMembers(String daoId) async {
    final resp = await _request('GET', '/daos/${Uri.encodeComponent(daoId)}/members');
    return (resp['members'] as List).cast<String>();
  }

  // ==================== Vesting Operations ====================

  Future<VestingContract> createVestingSchedule(VestingSchedule schedule) async {
    final resp = await _request('POST', '/vesting', schedule.toJson());
    return VestingContract.fromJson(resp);
  }

  Future<VestingContract> getVestingContract(String contractId) async {
    final resp = await _request('GET', '/vesting/${Uri.encodeComponent(contractId)}');
    return VestingContract.fromJson(resp);
  }

  Future<List<VestingContract>> listVestingContracts({String? beneficiary}) async {
    final path = beneficiary != null ? '/vesting?beneficiary=${Uri.encodeComponent(beneficiary)}' : '/vesting';
    final resp = await _request('GET', path);
    return (resp['contracts'] as List).map((e) => VestingContract.fromJson(e)).toList();
  }

  Future<ClaimReceipt> claimVested(String contractId) async {
    final resp = await _request('POST', '/vesting/${Uri.encodeComponent(contractId)}/claim');
    return ClaimReceipt.fromJson(resp);
  }

  Future<VestingContract> revokeVesting(String contractId) async {
    final resp = await _request('POST', '/vesting/${Uri.encodeComponent(contractId)}/revoke');
    return VestingContract.fromJson(resp);
  }

  Future<String> getReleasableAmount(String contractId) async {
    final resp = await _request('GET', '/vesting/${Uri.encodeComponent(contractId)}/releasable');
    return resp['amount'] as String;
  }

  // ==================== 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 GovernanceException('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 GovernanceException('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 'DELETE':
        response = await _client.delete(uri, headers: headers).timeout(config.timeout);
        break;
      default:
        throw GovernanceException('Unknown method: $method');
    }

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