"""
Synor Bridge SDK Types

Cross-chain asset transfers with lock-mint and burn-unlock patterns.
"""

from dataclasses import dataclass, field
from typing import Optional, List, Dict, Any, Literal
from enum import Enum


# ==================== Chain Types ====================

ChainId = Literal[
    "synor",
    "ethereum",
    "polygon",
    "arbitrum",
    "optimism",
    "bsc",
    "avalanche",
    "solana",
    "cosmos",
]


@dataclass
class NativeCurrency:
    """Native currency info for a chain."""
    name: str
    symbol: str
    decimals: int


@dataclass
class Chain:
    """Blockchain network information."""
    id: ChainId
    name: str
    chain_id: int
    rpc_url: str
    explorer_url: str
    native_currency: NativeCurrency
    confirmations: int
    estimated_block_time: int  # seconds
    supported: bool

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Chain":
        native = data.get("nativeCurrency", data.get("native_currency", {}))
        return cls(
            id=data["id"],
            name=data["name"],
            chain_id=data.get("chainId", data.get("chain_id", 0)),
            rpc_url=data.get("rpcUrl", data.get("rpc_url", "")),
            explorer_url=data.get("explorerUrl", data.get("explorer_url", "")),
            native_currency=NativeCurrency(
                name=native.get("name", ""),
                symbol=native.get("symbol", ""),
                decimals=native.get("decimals", 18),
            ),
            confirmations=data.get("confirmations", 0),
            estimated_block_time=data.get("estimatedBlockTime", data.get("estimated_block_time", 0)),
            supported=data.get("supported", True),
        )


# ==================== Asset Types ====================

class AssetType(str, Enum):
    """Asset type enumeration."""
    NATIVE = "native"
    ERC20 = "erc20"
    ERC721 = "erc721"
    ERC1155 = "erc1155"


@dataclass
class Asset:
    """Asset information."""
    id: str
    symbol: str
    name: str
    type: AssetType
    chain: ChainId
    decimals: int
    contract_address: Optional[str] = None
    logo_url: Optional[str] = None
    verified: bool = False

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Asset":
        return cls(
            id=data["id"],
            symbol=data["symbol"],
            name=data["name"],
            type=AssetType(data["type"]),
            chain=data["chain"],
            decimals=data.get("decimals", 18),
            contract_address=data.get("contractAddress", data.get("contract_address")),
            logo_url=data.get("logoUrl", data.get("logo_url")),
            verified=data.get("verified", False),
        )


@dataclass
class WrappedAsset:
    """Wrapped asset mapping."""
    original_asset: Asset
    wrapped_asset: Asset
    chain: ChainId
    bridge_contract: str

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "WrappedAsset":
        return cls(
            original_asset=Asset.from_dict(data.get("originalAsset", data.get("original_asset", {}))),
            wrapped_asset=Asset.from_dict(data.get("wrappedAsset", data.get("wrapped_asset", {}))),
            chain=data["chain"],
            bridge_contract=data.get("bridgeContract", data.get("bridge_contract", "")),
        )


# ==================== Transfer Types ====================

class TransferStatus(str, Enum):
    """Transfer status enumeration."""
    PENDING = "pending"
    LOCKED = "locked"
    CONFIRMING = "confirming"
    MINTING = "minting"
    COMPLETED = "completed"
    FAILED = "failed"
    REFUNDED = "refunded"


class TransferDirection(str, Enum):
    """Transfer direction enumeration."""
    LOCK_MINT = "lock_mint"
    BURN_UNLOCK = "burn_unlock"


@dataclass
class ValidatorSignature:
    """Validator signature for proof."""
    validator: str
    signature: str
    timestamp: int

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ValidatorSignature":
        return cls(
            validator=data["validator"],
            signature=data["signature"],
            timestamp=data["timestamp"],
        )


@dataclass
class LockReceipt:
    """Lock receipt from source chain."""
    id: str
    tx_hash: str
    source_chain: ChainId
    target_chain: ChainId
    asset: Asset
    amount: str
    sender: str
    recipient: str
    lock_timestamp: int
    confirmations: int
    required_confirmations: int

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "LockReceipt":
        return cls(
            id=data["id"],
            tx_hash=data.get("txHash", data.get("tx_hash", "")),
            source_chain=data.get("sourceChain", data.get("source_chain", "")),
            target_chain=data.get("targetChain", data.get("target_chain", "")),
            asset=Asset.from_dict(data["asset"]),
            amount=data["amount"],
            sender=data["sender"],
            recipient=data["recipient"],
            lock_timestamp=data.get("lockTimestamp", data.get("lock_timestamp", 0)),
            confirmations=data.get("confirmations", 0),
            required_confirmations=data.get("requiredConfirmations", data.get("required_confirmations", 0)),
        )


@dataclass
class LockProof:
    """Proof for minting on target chain."""
    lock_receipt: LockReceipt
    merkle_proof: List[str]
    block_header: str
    signatures: List[ValidatorSignature]

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "LockProof":
        return cls(
            lock_receipt=LockReceipt.from_dict(data.get("lockReceipt", data.get("lock_receipt", {}))),
            merkle_proof=data.get("merkleProof", data.get("merkle_proof", [])),
            block_header=data.get("blockHeader", data.get("block_header", "")),
            signatures=[ValidatorSignature.from_dict(s) for s in data.get("signatures", [])],
        )

    def to_dict(self) -> Dict[str, Any]:
        return {
            "lockReceipt": {
                "id": self.lock_receipt.id,
                "txHash": self.lock_receipt.tx_hash,
                "sourceChain": self.lock_receipt.source_chain,
                "targetChain": self.lock_receipt.target_chain,
                "asset": {
                    "id": self.lock_receipt.asset.id,
                    "symbol": self.lock_receipt.asset.symbol,
                    "name": self.lock_receipt.asset.name,
                    "type": self.lock_receipt.asset.type.value,
                    "chain": self.lock_receipt.asset.chain,
                    "decimals": self.lock_receipt.asset.decimals,
                },
                "amount": self.lock_receipt.amount,
                "sender": self.lock_receipt.sender,
                "recipient": self.lock_receipt.recipient,
                "lockTimestamp": self.lock_receipt.lock_timestamp,
                "confirmations": self.lock_receipt.confirmations,
                "requiredConfirmations": self.lock_receipt.required_confirmations,
            },
            "merkleProof": self.merkle_proof,
            "blockHeader": self.block_header,
            "signatures": [
                {"validator": s.validator, "signature": s.signature, "timestamp": s.timestamp}
                for s in self.signatures
            ],
        }


@dataclass
class BurnReceipt:
    """Burn receipt for unlocking."""
    id: str
    tx_hash: str
    source_chain: ChainId
    target_chain: ChainId
    wrapped_asset: Asset
    original_asset: Asset
    amount: str
    sender: str
    recipient: str
    burn_timestamp: int
    confirmations: int
    required_confirmations: int

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "BurnReceipt":
        return cls(
            id=data["id"],
            tx_hash=data.get("txHash", data.get("tx_hash", "")),
            source_chain=data.get("sourceChain", data.get("source_chain", "")),
            target_chain=data.get("targetChain", data.get("target_chain", "")),
            wrapped_asset=Asset.from_dict(data.get("wrappedAsset", data.get("wrapped_asset", {}))),
            original_asset=Asset.from_dict(data.get("originalAsset", data.get("original_asset", {}))),
            amount=data["amount"],
            sender=data["sender"],
            recipient=data["recipient"],
            burn_timestamp=data.get("burnTimestamp", data.get("burn_timestamp", 0)),
            confirmations=data.get("confirmations", 0),
            required_confirmations=data.get("requiredConfirmations", data.get("required_confirmations", 0)),
        )


@dataclass
class BurnProof:
    """Proof for unlocking on original chain."""
    burn_receipt: BurnReceipt
    merkle_proof: List[str]
    block_header: str
    signatures: List[ValidatorSignature]

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "BurnProof":
        return cls(
            burn_receipt=BurnReceipt.from_dict(data.get("burnReceipt", data.get("burn_receipt", {}))),
            merkle_proof=data.get("merkleProof", data.get("merkle_proof", [])),
            block_header=data.get("blockHeader", data.get("block_header", "")),
            signatures=[ValidatorSignature.from_dict(s) for s in data.get("signatures", [])],
        )

    def to_dict(self) -> Dict[str, Any]:
        return {
            "burnReceipt": {
                "id": self.burn_receipt.id,
                "txHash": self.burn_receipt.tx_hash,
                "sourceChain": self.burn_receipt.source_chain,
                "targetChain": self.burn_receipt.target_chain,
                "wrappedAsset": {
                    "id": self.burn_receipt.wrapped_asset.id,
                    "symbol": self.burn_receipt.wrapped_asset.symbol,
                    "name": self.burn_receipt.wrapped_asset.name,
                    "type": self.burn_receipt.wrapped_asset.type.value,
                    "chain": self.burn_receipt.wrapped_asset.chain,
                    "decimals": self.burn_receipt.wrapped_asset.decimals,
                },
                "originalAsset": {
                    "id": self.burn_receipt.original_asset.id,
                    "symbol": self.burn_receipt.original_asset.symbol,
                    "name": self.burn_receipt.original_asset.name,
                    "type": self.burn_receipt.original_asset.type.value,
                    "chain": self.burn_receipt.original_asset.chain,
                    "decimals": self.burn_receipt.original_asset.decimals,
                },
                "amount": self.burn_receipt.amount,
                "sender": self.burn_receipt.sender,
                "recipient": self.burn_receipt.recipient,
                "burnTimestamp": self.burn_receipt.burn_timestamp,
                "confirmations": self.burn_receipt.confirmations,
                "requiredConfirmations": self.burn_receipt.required_confirmations,
            },
            "merkleProof": self.merkle_proof,
            "blockHeader": self.block_header,
            "signatures": [
                {"validator": s.validator, "signature": s.signature, "timestamp": s.timestamp}
                for s in self.signatures
            ],
        }


@dataclass
class Transfer:
    """Complete transfer record."""
    id: str
    direction: TransferDirection
    status: TransferStatus
    source_chain: ChainId
    target_chain: ChainId
    asset: Asset
    amount: str
    sender: str
    recipient: str
    fee: str
    fee_asset: Asset
    created_at: int
    updated_at: int
    source_tx_hash: Optional[str] = None
    target_tx_hash: Optional[str] = None
    completed_at: Optional[int] = None
    error_message: Optional[str] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Transfer":
        return cls(
            id=data["id"],
            direction=TransferDirection(data["direction"]),
            status=TransferStatus(data["status"]),
            source_chain=data.get("sourceChain", data.get("source_chain", "")),
            target_chain=data.get("targetChain", data.get("target_chain", "")),
            asset=Asset.from_dict(data["asset"]),
            amount=data["amount"],
            sender=data["sender"],
            recipient=data["recipient"],
            fee=data["fee"],
            fee_asset=Asset.from_dict(data.get("feeAsset", data.get("fee_asset", {}))),
            created_at=data.get("createdAt", data.get("created_at", 0)),
            updated_at=data.get("updatedAt", data.get("updated_at", 0)),
            source_tx_hash=data.get("sourceTxHash", data.get("source_tx_hash")),
            target_tx_hash=data.get("targetTxHash", data.get("target_tx_hash")),
            completed_at=data.get("completedAt", data.get("completed_at")),
            error_message=data.get("errorMessage", data.get("error_message")),
        )


# ==================== Fee Types ====================

@dataclass
class FeeEstimate:
    """Fee estimate for bridge transfer."""
    bridge_fee: str
    gas_fee_source: str
    gas_fee_target: str
    total_fee: str
    fee_asset: Asset
    estimated_time: int  # seconds
    exchange_rate: Optional[str] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "FeeEstimate":
        return cls(
            bridge_fee=data.get("bridgeFee", data.get("bridge_fee", "0")),
            gas_fee_source=data.get("gasFeeSource", data.get("gas_fee_source", "0")),
            gas_fee_target=data.get("gasFeeTarget", data.get("gas_fee_target", "0")),
            total_fee=data.get("totalFee", data.get("total_fee", "0")),
            fee_asset=Asset.from_dict(data.get("feeAsset", data.get("fee_asset", {}))),
            estimated_time=data.get("estimatedTime", data.get("estimated_time", 0)),
            exchange_rate=data.get("exchangeRate", data.get("exchange_rate")),
        )


@dataclass
class ExchangeRate:
    """Exchange rate between assets."""
    from_asset: Asset
    to_asset: Asset
    rate: str
    inverse_rate: str
    last_updated: int
    source: str

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ExchangeRate":
        return cls(
            from_asset=Asset.from_dict(data.get("fromAsset", data.get("from_asset", {}))),
            to_asset=Asset.from_dict(data.get("toAsset", data.get("to_asset", {}))),
            rate=data["rate"],
            inverse_rate=data.get("inverseRate", data.get("inverse_rate", "")),
            last_updated=data.get("lastUpdated", data.get("last_updated", 0)),
            source=data.get("source", ""),
        )


# ==================== Transaction Types ====================

@dataclass
class SignedTransaction:
    """Signed transaction result."""
    tx_hash: str
    chain: ChainId
    from_address: str
    to_address: str
    value: str
    data: str
    gas_limit: str
    nonce: int
    signature: str
    gas_price: Optional[str] = None
    max_fee_per_gas: Optional[str] = None
    max_priority_fee_per_gas: Optional[str] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "SignedTransaction":
        return cls(
            tx_hash=data.get("txHash", data.get("tx_hash", "")),
            chain=data["chain"],
            from_address=data.get("from", data.get("from_address", "")),
            to_address=data.get("to", data.get("to_address", "")),
            value=data["value"],
            data=data["data"],
            gas_limit=data.get("gasLimit", data.get("gas_limit", "")),
            nonce=data["nonce"],
            signature=data["signature"],
            gas_price=data.get("gasPrice", data.get("gas_price")),
            max_fee_per_gas=data.get("maxFeePerGas", data.get("max_fee_per_gas")),
            max_priority_fee_per_gas=data.get("maxPriorityFeePerGas", data.get("max_priority_fee_per_gas")),
        )


# ==================== Filter Types ====================

@dataclass
class TransferFilter:
    """Filter for listing transfers."""
    status: Optional[TransferStatus] = None
    source_chain: Optional[ChainId] = None
    target_chain: Optional[ChainId] = None
    asset: Optional[str] = None
    sender: Optional[str] = None
    recipient: Optional[str] = None
    from_date: Optional[int] = None
    to_date: Optional[int] = None
    limit: Optional[int] = None
    offset: Optional[int] = None


# ==================== Options Types ====================

@dataclass
class LockOptions:
    """Options for lock operation."""
    recipient: Optional[str] = None
    deadline: Optional[int] = None
    slippage: Optional[float] = None


@dataclass
class MintOptions:
    """Options for mint operation."""
    gas_limit: Optional[str] = None
    max_fee_per_gas: Optional[str] = None
    max_priority_fee_per_gas: Optional[str] = None


@dataclass
class BurnOptions:
    """Options for burn operation."""
    recipient: Optional[str] = None
    deadline: Optional[int] = None


@dataclass
class UnlockOptions:
    """Options for unlock operation."""
    gas_limit: Optional[str] = None
    gas_price: Optional[str] = None


# ==================== Config Types ====================

DEFAULT_BRIDGE_ENDPOINT = "https://bridge.synor.io/v1"


@dataclass
class BridgeConfig:
    """Bridge configuration."""
    api_key: str
    endpoint: str = DEFAULT_BRIDGE_ENDPOINT
    timeout: float = 60.0
    retries: int = 3
    debug: bool = False


# ==================== Error Types ====================

class BridgeError(Exception):
    """Bridge-specific error."""

    def __init__(
        self,
        message: str,
        code: Optional[str] = None,
        status_code: Optional[int] = None,
        details: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(message)
        self.message = message
        self.code = code
        self.status_code = status_code
        self.details = details or {}
