import Foundation

// MARK: - Configuration

/// Configuration for the Synor Governance client.
public struct GovernanceConfig {
    public let apiKey: String
    public let endpoint: String
    public let timeout: TimeInterval
    public let retries: Int
    public let debug: Bool

    public init(
        apiKey: String,
        endpoint: String = "https://governance.synor.io/v1",
        timeout: TimeInterval = 60,
        retries: Int = 3,
        debug: Bool = false
    ) {
        self.apiKey = apiKey
        self.endpoint = endpoint
        self.timeout = timeout
        self.retries = retries
        self.debug = debug
    }
}

// MARK: - Enums

/// Proposal status.
public enum ProposalStatus: String, Codable, Sendable {
    case draft
    case active
    case passed
    case rejected
    case executed
    case cancelled
}

/// Vote choice.
public enum VoteChoice: String, Codable, Sendable {
    case yes
    case no
    case abstain
}

/// DAO type.
public enum DaoType: String, Codable, Sendable {
    case token
    case multisig
    case hybrid
}

/// Vesting status.
public enum VestingStatus: String, Codable, Sendable {
    case pending
    case active
    case paused
    case completed
    case revoked
}

// MARK: - Proposal Types

/// Proposal action.
public struct ProposalAction: Codable, Sendable {
    public var target: String
    public var method: String
    public var data: String
    public var value: String?

    public init(target: String, method: String, data: String, value: String? = nil) {
        self.target = target
        self.method = method
        self.data = data
        self.value = value
    }
}

/// Proposal draft.
public struct ProposalDraft: Codable, Sendable {
    public var title: String
    public var description: String
    public var discussionUrl: String?
    public var votingStartTime: Int64?
    public var votingEndTime: Int64?
    public var actions: [ProposalAction]?
    public var daoId: String?

    public init(
        title: String,
        description: String,
        discussionUrl: String? = nil,
        votingStartTime: Int64? = nil,
        votingEndTime: Int64? = nil,
        actions: [ProposalAction]? = nil,
        daoId: String? = nil
    ) {
        self.title = title
        self.description = description
        self.discussionUrl = discussionUrl
        self.votingStartTime = votingStartTime
        self.votingEndTime = votingEndTime
        self.actions = actions
        self.daoId = daoId
    }
}

/// Vote breakdown.
public struct VoteBreakdown: Codable, Sendable {
    public let yes: String
    public let no: String
    public let abstain: String
    public let quorum: String
    public let quorumPercent: Double
    public let quorumReached: Bool
}

/// Proposal.
public struct Proposal: Codable, Sendable {
    public let id: String
    public let title: String
    public let description: String
    public let proposer: String
    public let status: ProposalStatus
    public let createdAt: Int64
    public let votingStartTime: Int64
    public let votingEndTime: Int64
    public let votes: VoteBreakdown
    public let discussionUrl: String?
    public let actions: [ProposalAction]?
    public let daoId: String?
    public let snapshotBlock: String?
    public let executedAt: Int64?
    public let executedTxHash: String?
}

/// Proposal filter.
public struct ProposalFilter {
    public var status: ProposalStatus?
    public var proposer: String?
    public var daoId: String?
    public var limit: Int?
    public var offset: Int?

    public init(
        status: ProposalStatus? = nil,
        proposer: String? = nil,
        daoId: String? = nil,
        limit: Int? = nil,
        offset: Int? = nil
    ) {
        self.status = status
        self.proposer = proposer
        self.daoId = daoId
        self.limit = limit
        self.offset = offset
    }
}

// MARK: - Voting Types

/// Vote.
public struct Vote {
    public var choice: VoteChoice
    public var reason: String?

    public init(choice: VoteChoice, reason: String? = nil) {
        self.choice = choice
        self.reason = reason
    }
}

/// Vote receipt.
public struct VoteReceipt: Codable, Sendable {
    public let id: String
    public let proposalId: String
    public let voter: String
    public let choice: VoteChoice
    public let weight: String
    public let timestamp: Int64
    public let txHash: String
    public let reason: String?
}

/// Voting power.
public struct VotingPower: Codable, Sendable {
    public let address: String
    public let balance: String
    public let delegatedTo: String?
    public let delegatedFrom: String
    public let totalPower: String
    public let blockNumber: Int64
}

/// Delegation receipt.
public struct DelegationReceipt: Codable, Sendable {
    public let id: String
    public let delegator: String
    public let delegatee: String
    public let amount: String
    public let timestamp: Int64
    public let txHash: String
}

// MARK: - DAO Types

/// DAO configuration.
public struct DaoConfig {
    public var name: String
    public var description: String
    public var type: DaoType
    public var tokenAddress: String?
    public var quorumPercent: String?
    public var votingPeriodDays: Int
    public var timelockDays: Int
    public var proposalThreshold: Int?
    public var multisigMembers: [String]?
    public var multisigThreshold: Int?

    public init(
        name: String,
        description: String,
        type: DaoType,
        tokenAddress: String? = nil,
        quorumPercent: String? = nil,
        votingPeriodDays: Int = 7,
        timelockDays: Int = 2,
        proposalThreshold: Int? = nil,
        multisigMembers: [String]? = nil,
        multisigThreshold: Int? = nil
    ) {
        self.name = name
        self.description = description
        self.type = type
        self.tokenAddress = tokenAddress
        self.quorumPercent = quorumPercent
        self.votingPeriodDays = votingPeriodDays
        self.timelockDays = timelockDays
        self.proposalThreshold = proposalThreshold
        self.multisigMembers = multisigMembers
        self.multisigThreshold = multisigThreshold
    }
}

/// Treasury asset.
public struct TreasuryAsset: Codable, Sendable {
    public let address: String
    public let symbol: String
    public let balance: String
    public let valueUsd: String
}

/// DAO treasury.
public struct DaoTreasury: Codable, Sendable {
    public let address: String
    public let balance: String
    public let currency: String
    public let assets: [TreasuryAsset]
}

/// DAO.
public struct Dao: Codable, Sendable {
    public let id: String
    public let name: String
    public let description: String
    public let type: DaoType
    public let tokenAddress: String?
    public let governorAddress: String
    public let timelockAddress: String
    public let treasury: DaoTreasury
    public let quorumPercent: String
    public let votingPeriodDays: Int
    public let timelockDays: Int
    public let proposalCount: Int
    public let memberCount: Int
    public let createdAt: Int64
}

// MARK: - Vesting Types

/// Vesting schedule.
public struct VestingSchedule {
    public var beneficiary: String
    public var totalAmount: String
    public var startTime: Int64
    public var cliffDuration: Int64
    public var vestingDuration: Int64
    public var revocable: Bool

    public init(
        beneficiary: String,
        totalAmount: String,
        startTime: Int64,
        cliffDuration: Int64,
        vestingDuration: Int64,
        revocable: Bool = false
    ) {
        self.beneficiary = beneficiary
        self.totalAmount = totalAmount
        self.startTime = startTime
        self.cliffDuration = cliffDuration
        self.vestingDuration = vestingDuration
        self.revocable = revocable
    }
}

/// Vesting contract.
public struct VestingContract: Codable, Sendable {
    public let id: String
    public let contractAddress: String
    public let beneficiary: String
    public let totalAmount: String
    public let releasedAmount: String
    public let releasableAmount: String
    public let startTime: Int64
    public let cliffEnd: Int64
    public let vestingEnd: Int64
    public let status: VestingStatus
    public let createdAt: Int64
    public let txHash: String
}

/// Claim receipt.
public struct ClaimReceipt: Codable, Sendable {
    public let vestingContractId: String
    public let amount: String
    public let txHash: String
    public let timestamp: Int64
    public let remainingAmount: String
}

// MARK: - Response Types

struct ProposalsResponse: Codable {
    let proposals: [Proposal]?
}

struct VotesResponse: Codable {
    let votes: [VoteReceipt]?
}

struct DelegationsResponse: Codable {
    let delegations: [DelegationReceipt]?
}

struct DaosResponse: Codable {
    let daos: [Dao]?
}

struct MembersResponse: Codable {
    let members: [String]?
}

struct VestingContractsResponse: Codable {
    let contracts: [VestingContract]?
}

// MARK: - Error

/// Error thrown by Governance operations.
public struct GovernanceError: Error, LocalizedError {
    public let message: String
    public let code: String?
    public let statusCode: Int

    public init(message: String, code: String? = nil, statusCode: Int = 0) {
        self.message = message
        self.code = code
        self.statusCode = statusCode
    }

    public var errorDescription: String? { message }
}
