import Foundation

// MARK: - Configuration

/// Configuration for the Synor Bridge client.
public struct BridgeConfig {
    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://bridge.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: - Chain Types

/// Supported blockchain identifiers.
public enum ChainId: String, Codable, Sendable {
    case synor
    case ethereum
    case polygon
    case arbitrum
    case optimism
    case bsc
    case avalanche
    case solana
    case cosmos
}

/// A blockchain network.
public struct Chain: Codable, Sendable {
    public let id: ChainId
    public let name: String
    public let chainId: Int64
    public let rpcUrl: String
    public let explorerUrl: String
    public let nativeCurrency: NativeCurrency
    public let confirmations: Int
    public let estimatedBlockTime: Int
    public let supported: Bool
}

/// Native currency of a chain.
public struct NativeCurrency: Codable, Sendable {
    public let name: String
    public let symbol: String
    public let decimals: Int
}

/// Response for chain list operations.
public struct ChainsResponse: Codable {
    public let chains: [Chain]?
}

// MARK: - Asset Types

/// Type of asset.
public enum AssetType: String, Codable, Sendable {
    case native
    case erc20
    case erc721
    case erc1155
}

/// An asset on a blockchain.
public struct Asset: Codable, Sendable {
    public let id: String
    public let symbol: String
    public let name: String
    public let type: AssetType
    public let chain: ChainId
    public let contractAddress: String?
    public let decimals: Int
    public let logoUrl: String?
    public let verified: Bool
}

/// A wrapped asset on a target chain.
public struct WrappedAsset: Codable, Sendable {
    public let originalAsset: Asset
    public let wrappedAsset: Asset
    public let chain: ChainId
    public let bridgeContract: String
}

/// Response for asset list operations.
public struct AssetsResponse: Codable {
    public let assets: [Asset]?
}

// MARK: - Transfer Types

/// Status of a transfer.
public enum TransferStatus: String, Codable, Sendable {
    case pending
    case locked
    case confirming
    case minting
    case completed
    case failed
    case refunded
}

/// Direction of a transfer.
public enum TransferDirection: String, Codable, Sendable {
    case lock_mint
    case burn_unlock
}

/// A cross-chain transfer.
public struct Transfer: Codable, Sendable {
    public let id: String
    public let direction: TransferDirection
    public let status: TransferStatus
    public let sourceChain: ChainId
    public let targetChain: ChainId
    public let asset: Asset
    public let amount: String
    public let sender: String
    public let recipient: String
    public let sourceTxHash: String?
    public let targetTxHash: String?
    public let fee: String
    public let feeAsset: Asset
    public let createdAt: Int64
    public let updatedAt: Int64
    public let completedAt: Int64?
    public let errorMessage: String?
}

/// Filter for listing transfers.
public struct TransferFilter {
    public let status: TransferStatus?
    public let sourceChain: ChainId?
    public let targetChain: ChainId?
    public let asset: String?
    public let sender: String?
    public let recipient: String?
    public let limit: Int?
    public let offset: Int?

    public init(
        status: TransferStatus? = nil,
        sourceChain: ChainId? = nil,
        targetChain: ChainId? = nil,
        asset: String? = nil,
        sender: String? = nil,
        recipient: String? = nil,
        limit: Int? = nil,
        offset: Int? = nil
    ) {
        self.status = status
        self.sourceChain = sourceChain
        self.targetChain = targetChain
        self.asset = asset
        self.sender = sender
        self.recipient = recipient
        self.limit = limit
        self.offset = offset
    }
}

/// Response for transfer list operations.
public struct TransfersResponse: Codable {
    public let transfers: [Transfer]?
}

// MARK: - Lock-Mint Types

/// Validator signature for proofs.
public struct ValidatorSignature: Codable, Sendable {
    public let validator: String
    public let signature: String
    public let timestamp: Int64
}

/// Receipt from locking assets.
public struct LockReceipt: Codable, Sendable {
    public let id: String
    public let txHash: String
    public let sourceChain: ChainId
    public let targetChain: ChainId
    public let asset: Asset
    public let amount: String
    public let sender: String
    public let recipient: String
    public let lockTimestamp: Int64
    public let confirmations: Int
    public let requiredConfirmations: Int
}

/// Proof of lock for minting.
public struct LockProof: Codable, Sendable {
    public let lockReceipt: LockReceipt
    public let merkleProof: [String]
    public let blockHeader: String
    public let signatures: [ValidatorSignature]
}

/// Options for locking assets.
public struct LockOptions: Codable, Sendable {
    public let recipient: String?
    public let deadline: Int64?
    public let slippage: Double?

    public init(recipient: String? = nil, deadline: Int64? = nil, slippage: Double? = nil) {
        self.recipient = recipient
        self.deadline = deadline
        self.slippage = slippage
    }
}

/// Options for minting assets.
public struct MintOptions: Codable, Sendable {
    public let gasLimit: String?
    public let maxFeePerGas: String?
    public let maxPriorityFeePerGas: String?

    public init(gasLimit: String? = nil, maxFeePerGas: String? = nil, maxPriorityFeePerGas: String? = nil) {
        self.gasLimit = gasLimit
        self.maxFeePerGas = maxFeePerGas
        self.maxPriorityFeePerGas = maxPriorityFeePerGas
    }
}

// MARK: - Burn-Unlock Types

/// Receipt from burning wrapped assets.
public struct BurnReceipt: Codable, Sendable {
    public let id: String
    public let txHash: String
    public let sourceChain: ChainId
    public let targetChain: ChainId
    public let wrappedAsset: Asset
    public let originalAsset: Asset
    public let amount: String
    public let sender: String
    public let recipient: String
    public let burnTimestamp: Int64
    public let confirmations: Int
    public let requiredConfirmations: Int
}

/// Proof of burn for unlocking.
public struct BurnProof: Codable, Sendable {
    public let burnReceipt: BurnReceipt
    public let merkleProof: [String]
    public let blockHeader: String
    public let signatures: [ValidatorSignature]
}

/// Options for burning wrapped assets.
public struct BurnOptions: Codable, Sendable {
    public let recipient: String?
    public let deadline: Int64?

    public init(recipient: String? = nil, deadline: Int64? = nil) {
        self.recipient = recipient
        self.deadline = deadline
    }
}

/// Options for unlocking original assets.
public struct UnlockOptions: Codable, Sendable {
    public let gasLimit: String?
    public let gasPrice: String?

    public init(gasLimit: String? = nil, gasPrice: String? = nil) {
        self.gasLimit = gasLimit
        self.gasPrice = gasPrice
    }
}

// MARK: - Fee & Rate Types

/// Fee estimate for a transfer.
public struct BridgeFeeEstimate: Codable, Sendable {
    public let bridgeFee: String
    public let gasFeeSource: String
    public let gasFeeTarget: String
    public let totalFee: String
    public let feeAsset: Asset
    public let estimatedTime: Int
    public let exchangeRate: String?
}

/// Exchange rate between assets.
public struct ExchangeRate: Codable, Sendable {
    public let fromAsset: Asset
    public let toAsset: Asset
    public let rate: String
    public let inverseRate: String
    public let lastUpdated: Int64
    public let source: String
}

// MARK: - Transaction Types

/// A signed transaction.
public struct BridgeSignedTransaction: Codable, Sendable {
    public let txHash: String
    public let chain: ChainId
    public let from: String
    public let to: String
    public let value: String
    public let data: String
    public let gasLimit: String
    public let gasPrice: String?
    public let maxFeePerGas: String?
    public let maxPriorityFeePerGas: String?
    public let nonce: Int
    public let signature: String
}

// MARK: - Error Types

/// Errors that can occur during bridge operations.
public enum BridgeError: Error, LocalizedError {
    case clientClosed
    case httpError(statusCode: Int, message: String, code: String?)
    case networkError(Error)
    case encodingError(Error)
    case decodingError(Error)
    case invalidResponse
    case timeout(String)

    public var errorDescription: String? {
        switch self {
        case .clientClosed:
            return "Client has been closed"
        case .httpError(let statusCode, let message, _):
            return "HTTP \(statusCode): \(message)"
        case .networkError(let error):
            return "Network error: \(error.localizedDescription)"
        case .encodingError(let error):
            return "Encoding error: \(error.localizedDescription)"
        case .decodingError(let error):
            return "Decoding error: \(error.localizedDescription)"
        case .invalidResponse:
            return "Invalid response"
        case .timeout(let message):
            return "Timeout: \(message)"
        }
    }

    /// Whether this error indicates confirmations are still pending.
    public var isConfirmationsPending: Bool {
        if case .httpError(_, _, let code) = self {
            return code == "CONFIRMATIONS_PENDING"
        }
        return false
    }
}

// MARK: - Health Check

/// Health check response.
public struct BridgeHealthResponse: Codable {
    public let status: String
}
