/// Synor Governance SDK Types for Flutter
library synor_governance_types;

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

enum ProposalStatus { draft, active, passed, rejected, executed, cancelled }
enum VoteChoice { yes, no, abstain }
enum DaoType { token, multisig, hybrid }
enum VestingStatus { pending, active, paused, completed, revoked }

// ==================== Proposal Types ====================

class ProposalAction {
  final String target;
  final String method;
  final String data;
  final String? value;

  const ProposalAction({required this.target, required this.method, required this.data, this.value});

  Map<String, dynamic> toJson() => {'target': target, 'method': method, 'data': data, if (value != null) 'value': value};

  factory ProposalAction.fromJson(Map<String, dynamic> json) => ProposalAction(
        target: json['target'] as String,
        method: json['method'] as String,
        data: json['data'] as String,
        value: json['value'] as String?,
      );
}

class ProposalDraft {
  final String title;
  final String description;
  final String? discussionUrl;
  final int? votingStartTime;
  final int? votingEndTime;
  final List<ProposalAction>? actions;
  final String? daoId;

  const ProposalDraft({
    required this.title,
    required this.description,
    this.discussionUrl,
    this.votingStartTime,
    this.votingEndTime,
    this.actions,
    this.daoId,
  });

  Map<String, dynamic> toJson() => {
        'title': title,
        'description': description,
        if (discussionUrl != null) 'discussionUrl': discussionUrl,
        if (votingStartTime != null) 'votingStartTime': votingStartTime,
        if (votingEndTime != null) 'votingEndTime': votingEndTime,
        if (actions != null) 'actions': actions!.map((e) => e.toJson()).toList(),
        if (daoId != null) 'daoId': daoId,
      };
}

class VoteBreakdown {
  final String yes;
  final String no;
  final String abstain;
  final String quorum;
  final double quorumPercent;
  final bool quorumReached;

  const VoteBreakdown({
    required this.yes,
    required this.no,
    required this.abstain,
    required this.quorum,
    required this.quorumPercent,
    required this.quorumReached,
  });

  factory VoteBreakdown.fromJson(Map<String, dynamic> json) => VoteBreakdown(
        yes: json['yes'] as String,
        no: json['no'] as String,
        abstain: json['abstain'] as String,
        quorum: json['quorum'] as String,
        quorumPercent: (json['quorumPercent'] as num).toDouble(),
        quorumReached: json['quorumReached'] as bool,
      );
}

class Proposal {
  final String id;
  final String title;
  final String description;
  final String proposer;
  final ProposalStatus status;
  final int createdAt;
  final int votingStartTime;
  final int votingEndTime;
  final VoteBreakdown votes;
  final String? discussionUrl;
  final List<ProposalAction>? actions;
  final String? daoId;
  final String? snapshotBlock;
  final int? executedAt;
  final String? executedTxHash;

  const Proposal({
    required this.id,
    required this.title,
    required this.description,
    required this.proposer,
    required this.status,
    required this.createdAt,
    required this.votingStartTime,
    required this.votingEndTime,
    required this.votes,
    this.discussionUrl,
    this.actions,
    this.daoId,
    this.snapshotBlock,
    this.executedAt,
    this.executedTxHash,
  });

  factory Proposal.fromJson(Map<String, dynamic> json) => Proposal(
        id: json['id'] as String,
        title: json['title'] as String,
        description: json['description'] as String,
        proposer: json['proposer'] as String,
        status: ProposalStatus.values.firstWhere((e) => e.name == json['status']),
        createdAt: json['createdAt'] as int,
        votingStartTime: json['votingStartTime'] as int,
        votingEndTime: json['votingEndTime'] as int,
        votes: VoteBreakdown.fromJson(json['votes']),
        discussionUrl: json['discussionUrl'] as String?,
        actions: (json['actions'] as List?)?.map((e) => ProposalAction.fromJson(e)).toList(),
        daoId: json['daoId'] as String?,
        snapshotBlock: json['snapshotBlock'] as String?,
        executedAt: json['executedAt'] as int?,
        executedTxHash: json['executedTxHash'] as String?,
      );
}

class ProposalFilter {
  final ProposalStatus? status;
  final String? proposer;
  final String? daoId;
  final int? limit;
  final int? offset;

  const ProposalFilter({this.status, this.proposer, this.daoId, this.limit, this.offset});
}

// ==================== Voting Types ====================

class Vote {
  final VoteChoice choice;
  final String? reason;

  const Vote({required this.choice, this.reason});
}

class VoteReceipt {
  final String id;
  final String proposalId;
  final String voter;
  final VoteChoice choice;
  final String weight;
  final int timestamp;
  final String txHash;
  final String? reason;

  const VoteReceipt({
    required this.id,
    required this.proposalId,
    required this.voter,
    required this.choice,
    required this.weight,
    required this.timestamp,
    required this.txHash,
    this.reason,
  });

  factory VoteReceipt.fromJson(Map<String, dynamic> json) => VoteReceipt(
        id: json['id'] as String,
        proposalId: json['proposalId'] as String,
        voter: json['voter'] as String,
        choice: VoteChoice.values.firstWhere((e) => e.name == json['choice']),
        weight: json['weight'] as String,
        timestamp: json['timestamp'] as int,
        txHash: json['txHash'] as String,
        reason: json['reason'] as String?,
      );
}

class VotingPower {
  final String address;
  final String balance;
  final String? delegatedTo;
  final String delegatedFrom;
  final String totalPower;
  final int blockNumber;

  const VotingPower({
    required this.address,
    required this.balance,
    this.delegatedTo,
    required this.delegatedFrom,
    required this.totalPower,
    required this.blockNumber,
  });

  factory VotingPower.fromJson(Map<String, dynamic> json) => VotingPower(
        address: json['address'] as String,
        balance: json['balance'] as String,
        delegatedTo: json['delegatedTo'] as String?,
        delegatedFrom: json['delegatedFrom'] as String,
        totalPower: json['totalPower'] as String,
        blockNumber: json['blockNumber'] as int,
      );
}

class DelegationReceipt {
  final String id;
  final String delegator;
  final String delegatee;
  final String amount;
  final int timestamp;
  final String txHash;

  const DelegationReceipt({
    required this.id,
    required this.delegator,
    required this.delegatee,
    required this.amount,
    required this.timestamp,
    required this.txHash,
  });

  factory DelegationReceipt.fromJson(Map<String, dynamic> json) => DelegationReceipt(
        id: json['id'] as String,
        delegator: json['delegator'] as String,
        delegatee: json['delegatee'] as String,
        amount: json['amount'] as String,
        timestamp: json['timestamp'] as int,
        txHash: json['txHash'] as String,
      );
}

// ==================== DAO Types ====================

class DaoConfig {
  final String name;
  final String description;
  final DaoType type;
  final String? tokenAddress;
  final String? quorumPercent;
  final int votingPeriodDays;
  final int timelockDays;
  final int? proposalThreshold;
  final List<String>? multisigMembers;
  final int? multisigThreshold;

  const DaoConfig({
    required this.name,
    required this.description,
    required this.type,
    this.tokenAddress,
    this.quorumPercent,
    this.votingPeriodDays = 7,
    this.timelockDays = 2,
    this.proposalThreshold,
    this.multisigMembers,
    this.multisigThreshold,
  });

  Map<String, dynamic> toJson() => {
        'name': name,
        'description': description,
        'type': type.name,
        if (tokenAddress != null) 'tokenAddress': tokenAddress,
        if (quorumPercent != null) 'quorumPercent': quorumPercent,
        'votingPeriodDays': votingPeriodDays,
        'timelockDays': timelockDays,
        if (proposalThreshold != null) 'proposalThreshold': proposalThreshold,
        if (multisigMembers != null) 'multisigMembers': multisigMembers,
        if (multisigThreshold != null) 'multisigThreshold': multisigThreshold,
      };
}

class TreasuryAsset {
  final String address;
  final String symbol;
  final String balance;
  final String valueUsd;

  const TreasuryAsset({required this.address, required this.symbol, required this.balance, required this.valueUsd});

  factory TreasuryAsset.fromJson(Map<String, dynamic> json) => TreasuryAsset(
        address: json['address'] as String,
        symbol: json['symbol'] as String,
        balance: json['balance'] as String,
        valueUsd: json['valueUsd'] as String,
      );
}

class DaoTreasury {
  final String address;
  final String balance;
  final String currency;
  final List<TreasuryAsset> assets;

  const DaoTreasury({required this.address, required this.balance, required this.currency, required this.assets});

  factory DaoTreasury.fromJson(Map<String, dynamic> json) => DaoTreasury(
        address: json['address'] as String,
        balance: json['balance'] as String,
        currency: json['currency'] as String,
        assets: (json['assets'] as List).map((e) => TreasuryAsset.fromJson(e)).toList(),
      );
}

class Dao {
  final String id;
  final String name;
  final String description;
  final DaoType type;
  final String? tokenAddress;
  final String governorAddress;
  final String timelockAddress;
  final DaoTreasury treasury;
  final String quorumPercent;
  final int votingPeriodDays;
  final int timelockDays;
  final int proposalCount;
  final int memberCount;
  final int createdAt;

  const Dao({
    required this.id,
    required this.name,
    required this.description,
    required this.type,
    this.tokenAddress,
    required this.governorAddress,
    required this.timelockAddress,
    required this.treasury,
    required this.quorumPercent,
    required this.votingPeriodDays,
    required this.timelockDays,
    required this.proposalCount,
    required this.memberCount,
    required this.createdAt,
  });

  factory Dao.fromJson(Map<String, dynamic> json) => Dao(
        id: json['id'] as String,
        name: json['name'] as String,
        description: json['description'] as String,
        type: DaoType.values.firstWhere((e) => e.name == json['type']),
        tokenAddress: json['tokenAddress'] as String?,
        governorAddress: json['governorAddress'] as String,
        timelockAddress: json['timelockAddress'] as String,
        treasury: DaoTreasury.fromJson(json['treasury']),
        quorumPercent: json['quorumPercent'] as String,
        votingPeriodDays: json['votingPeriodDays'] as int,
        timelockDays: json['timelockDays'] as int,
        proposalCount: json['proposalCount'] as int,
        memberCount: json['memberCount'] as int,
        createdAt: json['createdAt'] as int,
      );
}

// ==================== Vesting Types ====================

class VestingSchedule {
  final String beneficiary;
  final String totalAmount;
  final int startTime;
  final int cliffDuration;
  final int vestingDuration;
  final bool revocable;

  const VestingSchedule({
    required this.beneficiary,
    required this.totalAmount,
    required this.startTime,
    required this.cliffDuration,
    required this.vestingDuration,
    this.revocable = false,
  });

  Map<String, dynamic> toJson() => {
        'beneficiary': beneficiary,
        'totalAmount': totalAmount,
        'startTime': startTime,
        'cliffDuration': cliffDuration,
        'vestingDuration': vestingDuration,
        'revocable': revocable,
      };
}

class VestingContract {
  final String id;
  final String contractAddress;
  final String beneficiary;
  final String totalAmount;
  final String releasedAmount;
  final String releasableAmount;
  final int startTime;
  final int cliffEnd;
  final int vestingEnd;
  final VestingStatus status;
  final int createdAt;
  final String txHash;

  const VestingContract({
    required this.id,
    required this.contractAddress,
    required this.beneficiary,
    required this.totalAmount,
    required this.releasedAmount,
    required this.releasableAmount,
    required this.startTime,
    required this.cliffEnd,
    required this.vestingEnd,
    required this.status,
    required this.createdAt,
    required this.txHash,
  });

  factory VestingContract.fromJson(Map<String, dynamic> json) => VestingContract(
        id: json['id'] as String,
        contractAddress: json['contractAddress'] as String,
        beneficiary: json['beneficiary'] as String,
        totalAmount: json['totalAmount'] as String,
        releasedAmount: json['releasedAmount'] as String,
        releasableAmount: json['releasableAmount'] as String,
        startTime: json['startTime'] as int,
        cliffEnd: json['cliffEnd'] as int,
        vestingEnd: json['vestingEnd'] as int,
        status: VestingStatus.values.firstWhere((e) => e.name == json['status']),
        createdAt: json['createdAt'] as int,
        txHash: json['txHash'] as String,
      );
}

class ClaimReceipt {
  final String vestingContractId;
  final String amount;
  final String txHash;
  final int timestamp;
  final String remainingAmount;

  const ClaimReceipt({
    required this.vestingContractId,
    required this.amount,
    required this.txHash,
    required this.timestamp,
    required this.remainingAmount,
  });

  factory ClaimReceipt.fromJson(Map<String, dynamic> json) => ClaimReceipt(
        vestingContractId: json['vestingContractId'] as String,
        amount: json['amount'] as String,
        txHash: json['txHash'] as String,
        timestamp: json['timestamp'] as int,
        remainingAmount: json['remainingAmount'] as String,
      );
}

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

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

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

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

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

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