"""
Synor Contract SDK Types

Smart contract types, ABI definitions, and transaction results.
"""

from dataclasses import dataclass, field
from typing import Optional, List, Dict, Any, Literal, Union, Callable


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

DEFAULT_CONTRACT_ENDPOINT = "https://contract.synor.cc/api/v1"


@dataclass
class ContractConfig:
    """Contract SDK configuration."""
    api_key: str
    endpoint: str = DEFAULT_CONTRACT_ENDPOINT
    timeout: float = 30.0
    retries: int = 3
    debug: bool = False
    default_gas_limit: Optional[str] = None
    default_gas_price: Optional[str] = None


class ContractError(Exception):
    """Contract-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 {}


# ==================== ABI Types ====================

AbiEntryType = Literal["function", "constructor", "event", "error", "fallback", "receive"]
StateMutability = Literal["pure", "view", "nonpayable", "payable"]


@dataclass
class AbiParameter:
    """ABI input/output parameter."""
    name: str
    type: str
    indexed: bool = False
    components: Optional[List["AbiParameter"]] = None
    internal_type: Optional[str] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "AbiParameter":
        components = None
        if "components" in data:
            components = [AbiParameter.from_dict(c) for c in data["components"]]
        return cls(
            name=data.get("name", ""),
            type=data["type"],
            indexed=data.get("indexed", False),
            components=components,
            internal_type=data.get("internalType", data.get("internal_type")),
        )

    def to_dict(self) -> Dict[str, Any]:
        d: Dict[str, Any] = {"name": self.name, "type": self.type}
        if self.indexed:
            d["indexed"] = self.indexed
        if self.components:
            d["components"] = [c.to_dict() for c in self.components]
        if self.internal_type:
            d["internalType"] = self.internal_type
        return d


@dataclass
class AbiEntry:
    """ABI entry."""
    type: AbiEntryType
    name: Optional[str] = None
    inputs: Optional[List[AbiParameter]] = None
    outputs: Optional[List[AbiParameter]] = None
    state_mutability: Optional[StateMutability] = None
    anonymous: bool = False

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "AbiEntry":
        inputs = None
        outputs = None
        if "inputs" in data:
            inputs = [AbiParameter.from_dict(i) for i in data["inputs"]]
        if "outputs" in data:
            outputs = [AbiParameter.from_dict(o) for o in data["outputs"]]
        return cls(
            type=data["type"],
            name=data.get("name"),
            inputs=inputs,
            outputs=outputs,
            state_mutability=data.get("stateMutability", data.get("state_mutability")),
            anonymous=data.get("anonymous", False),
        )

    def to_dict(self) -> Dict[str, Any]:
        d: Dict[str, Any] = {"type": self.type}
        if self.name:
            d["name"] = self.name
        if self.inputs:
            d["inputs"] = [i.to_dict() for i in self.inputs]
        if self.outputs:
            d["outputs"] = [o.to_dict() for o in self.outputs]
        if self.state_mutability:
            d["stateMutability"] = self.state_mutability
        if self.anonymous:
            d["anonymous"] = self.anonymous
        return d


# Type alias for ABI
Abi = List[AbiEntry]


# ==================== Deployment Types ====================

@dataclass
class DeploymentResult:
    """Deployment result."""
    address: str
    transaction_hash: str
    block_number: int
    gas_used: str
    effective_gas_price: str

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "DeploymentResult":
        return cls(
            address=data["address"],
            transaction_hash=data.get("transactionHash", data.get("transaction_hash", "")),
            block_number=data.get("blockNumber", data.get("block_number", 0)),
            gas_used=data.get("gasUsed", data.get("gas_used", "")),
            effective_gas_price=data.get("effectiveGasPrice", data.get("effective_gas_price", "")),
        )


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

TransactionStatus = Literal["success", "reverted"]


@dataclass
class EventLog:
    """Event log."""
    log_index: int
    address: str
    topics: List[str]
    data: str
    block_number: int
    transaction_hash: str

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "EventLog":
        return cls(
            log_index=data.get("logIndex", data.get("log_index", 0)),
            address=data["address"],
            topics=data["topics"],
            data=data["data"],
            block_number=data.get("blockNumber", data.get("block_number", 0)),
            transaction_hash=data.get("transactionHash", data.get("transaction_hash", "")),
        )

    def to_dict(self) -> Dict[str, Any]:
        return {
            "logIndex": self.log_index,
            "address": self.address,
            "topics": self.topics,
            "data": self.data,
            "blockNumber": self.block_number,
            "transactionHash": self.transaction_hash,
        }


@dataclass
class DecodedEvent:
    """Decoded event."""
    name: str
    signature: str
    args: Dict[str, Any]
    log: EventLog

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "DecodedEvent":
        return cls(
            name=data["name"],
            signature=data["signature"],
            args=data["args"],
            log=EventLog.from_dict(data["log"]),
        )


@dataclass
class TransactionResult:
    """Transaction result."""
    transaction_hash: str
    block_number: int
    block_hash: str
    gas_used: str
    effective_gas_price: str
    status: TransactionStatus
    logs: List[EventLog]
    return_value: Optional[Any] = None
    revert_reason: Optional[str] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "TransactionResult":
        return cls(
            transaction_hash=data.get("transactionHash", data.get("transaction_hash", "")),
            block_number=data.get("blockNumber", data.get("block_number", 0)),
            block_hash=data.get("blockHash", data.get("block_hash", "")),
            gas_used=data.get("gasUsed", data.get("gas_used", "")),
            effective_gas_price=data.get("effectiveGasPrice", data.get("effective_gas_price", "")),
            status=data["status"],
            logs=[EventLog.from_dict(l) for l in data.get("logs", [])],
            return_value=data.get("returnValue", data.get("return_value")),
            revert_reason=data.get("revertReason", data.get("revert_reason")),
        )


# ==================== Contract Interface Types ====================

@dataclass
class ContractInterface:
    """Contract interface (from ABI)."""
    abi: List[AbiEntry]
    functions: Dict[str, AbiEntry]
    events: Dict[str, AbiEntry]
    errors: Dict[str, AbiEntry]


# ==================== Gas Types ====================

@dataclass
class GasEstimation:
    """Gas estimation result."""
    gas_limit: str
    gas_price: str
    estimated_cost: str

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "GasEstimation":
        return cls(
            gas_limit=data.get("gasLimit", data.get("gas_limit", "")),
            gas_price=data.get("gasPrice", data.get("gas_price", "")),
            estimated_cost=data.get("estimatedCost", data.get("estimated_cost", "")),
        )


# ==================== Bytecode Types ====================

@dataclass
class BytecodeMetadata:
    """Bytecode metadata."""
    compiler: str
    language: str
    sources: List[str]

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "BytecodeMetadata":
        return cls(
            compiler=data.get("compiler", ""),
            language=data.get("language", ""),
            sources=data.get("sources", []),
        )


@dataclass
class BytecodeInfo:
    """Contract bytecode info."""
    bytecode: str
    deployed_bytecode: Optional[str] = None
    abi: Optional[List[AbiEntry]] = None
    metadata: Optional[BytecodeMetadata] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "BytecodeInfo":
        abi = None
        if "abi" in data:
            abi = [AbiEntry.from_dict(e) for e in data["abi"]]
        metadata = None
        if "metadata" in data:
            metadata = BytecodeMetadata.from_dict(data["metadata"])
        return cls(
            bytecode=data["bytecode"],
            deployed_bytecode=data.get("deployedBytecode", data.get("deployed_bytecode")),
            abi=abi,
            metadata=metadata,
        )


# ==================== Verification Types ====================

VerificationStatus = Literal["verified", "pending", "failed"]


@dataclass
class VerificationResult:
    """Contract verification result."""
    status: VerificationStatus
    message: str
    abi: Optional[List[AbiEntry]] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "VerificationResult":
        abi = None
        if "abi" in data:
            abi = [AbiEntry.from_dict(e) for e in data["abi"]]
        return cls(
            status=data["status"],
            message=data["message"],
            abi=abi,
        )


# ==================== Multicall Types ====================

@dataclass
class MulticallRequest:
    """Multicall request."""
    address: str
    call_data: str
    allow_failure: bool = False

    def to_dict(self) -> Dict[str, Any]:
        return {
            "address": self.address,
            "callData": self.call_data,
            "allowFailure": self.allow_failure,
        }


@dataclass
class MulticallResult:
    """Multicall result."""
    success: bool
    return_data: str
    decoded: Optional[Any] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "MulticallResult":
        return cls(
            success=data["success"],
            return_data=data.get("returnData", data.get("return_data", "")),
            decoded=data.get("decoded"),
        )


# ==================== Event Subscription Types ====================

# Callback type for event subscription
EventCallback = Callable[[DecodedEvent], None]


@dataclass
class EventSubscription:
    """Event subscription handle."""
    id: str
    unsubscribe: Callable[[], None]
