"""
Pydantic models for ARP SDK request and response data.
"""

from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Any, Generic, TypeVar

from pydantic import BaseModel, Field


# ──────────────────────────────────────────────
# Enums
# ──────────────────────────────────────────────


class AgentStatus(str, Enum):
    ACTIVE = "active"
    SUSPENDED = "suspended"
    DEREGISTERED = "deregistered"


class TransactionStatus(str, Enum):
    PENDING = "pending"
    ACCEPTED = "accepted"
    COMPLETED = "completed"
    DISPUTED = "disputed"
    CANCELLED = "cancelled"


class DisputeStatus(str, Enum):
    OPEN = "open"
    RESOLVED = "resolved"
    REJECTED = "rejected"


class WebhookEvent(str, Enum):
    TRANSACTION_CREATED = "transaction.created"
    TRANSACTION_ACCEPTED = "transaction.accepted"
    TRANSACTION_COMPLETED = "transaction.completed"
    TRANSACTION_DISPUTED = "transaction.disputed"
    REPUTATION_UPDATED = "reputation.updated"
    AGENT_REGISTERED = "agent.registered"
    AGENT_SUSPENDED = "agent.suspended"
    STAKING_DEPOSITED = "staking.deposited"
    STAKING_WITHDRAWN = "staking.withdrawn"


# ──────────────────────────────────────────────
# Common
# ──────────────────────────────────────────────

T = TypeVar("T")


class PaginationInfo(BaseModel):
    page: int
    limit: int
    total: int
    total_pages: int = Field(alias="totalPages")

    model_config = {"populate_by_name": True}


class PaginatedResponse(BaseModel, Generic[T]):
    data: list[T]
    pagination: PaginationInfo


# ──────────────────────────────────────────────
# Agent Models
# ──────────────────────────────────────────────


class Agent(BaseModel):
    id: str
    address: str
    name: str
    description: str
    metadata: dict[str, Any] = Field(default_factory=dict)
    status: AgentStatus
    domains: list[str] = Field(default_factory=list)
    created_at: datetime = Field(alias="createdAt")
    updated_at: datetime = Field(alias="updatedAt")

    model_config = {"populate_by_name": True}


class DomainReputation(BaseModel):
    domain: str
    score: float
    transaction_count: int = Field(alias="transactionCount")
    success_rate: float = Field(alias="successRate")
    average_rating: float = Field(alias="averageRating")

    model_config = {"populate_by_name": True}


class ReputationScore(BaseModel):
    agent_id: str = Field(alias="agentId")
    overall: float
    domains: dict[str, DomainReputation] = Field(default_factory=dict)
    total_transactions: int = Field(alias="totalTransactions")
    success_rate: float = Field(alias="successRate")
    last_updated: datetime = Field(alias="lastUpdated")

    model_config = {"populate_by_name": True}


class StakeInfo(BaseModel):
    agent_id: str = Field(alias="agentId")
    amount: str
    locked_until: datetime | None = Field(default=None, alias="lockedUntil")
    last_deposit_at: datetime | None = Field(default=None, alias="lastDepositAt")
    last_withdraw_at: datetime | None = Field(default=None, alias="lastWithdrawAt")

    model_config = {"populate_by_name": True}


class AgentWithDetails(Agent):
    reputation: ReputationScore
    stake: StakeInfo
    transaction_count: int = Field(alias="transactionCount")
    guilds: list[str] = Field(default_factory=list)

    model_config = {"populate_by_name": True}


# ──────────────────────────────────────────────
# Reputation Models
# ──────────────────────────────────────────────


class ReputationSnapshot(BaseModel):
    timestamp: datetime
    overall: float
    domains: dict[str, float] = Field(default_factory=dict)
    transaction_id: str | None = Field(default=None, alias="transactionId")
    change: float
    reason: str

    model_config = {"populate_by_name": True}


class ReputationHistory(BaseModel):
    agent_id: str = Field(alias="agentId")
    history: list[ReputationSnapshot]

    model_config = {"populate_by_name": True}


class ReputationAttestation(BaseModel):
    agent_id: str = Field(alias="agentId")
    score: float
    timestamp: datetime
    merkle_root: str = Field(alias="merkleRoot")
    merkle_proof: list[str] = Field(alias="merkleProof")
    signature: str

    model_config = {"populate_by_name": True}


# ──────────────────────────────────────────────
# Transaction Models
# ──────────────────────────────────────────────


class Transaction(BaseModel):
    id: str
    from_agent_id: str = Field(alias="fromAgentId")
    to_agent_id: str = Field(alias="toAgentId")
    domain: str
    amount: str
    description: str
    metadata: dict[str, Any] = Field(default_factory=dict)
    status: TransactionStatus
    rating: int | None = None
    feedback: str | None = None
    completed_at: datetime | None = Field(default=None, alias="completedAt")
    dispute_id: str | None = Field(default=None, alias="disputeId")
    created_at: datetime = Field(alias="createdAt")
    updated_at: datetime = Field(alias="updatedAt")

    model_config = {"populate_by_name": True}


class Dispute(BaseModel):
    id: str
    transaction_id: str = Field(alias="transactionId")
    initiator_id: str = Field(alias="initiatorId")
    reason: str
    evidence: dict[str, Any] | None = None
    status: DisputeStatus
    resolution: str | None = None
    resolved_at: datetime | None = Field(default=None, alias="resolvedAt")
    created_at: datetime = Field(alias="createdAt")
    updated_at: datetime = Field(alias="updatedAt")

    model_config = {"populate_by_name": True}


class TransactionVerification(BaseModel):
    transaction_id: str = Field(alias="transactionId")
    verified: bool
    merkle_proof: list[str] = Field(alias="merkleProof")
    merkle_root: str = Field(alias="merkleRoot")
    block_number: int | None = Field(default=None, alias="blockNumber")
    tx_hash: str | None = Field(default=None, alias="txHash")

    model_config = {"populate_by_name": True}


# ──────────────────────────────────────────────
# Staking Models
# ──────────────────────────────────────────────


class StakeCalculation(BaseModel):
    agent_id: str = Field(alias="agentId")
    current_stake: str = Field(alias="currentStake")
    required_stake: str = Field(alias="requiredStake")
    reputation_bonus: float = Field(alias="reputationBonus")
    effective_reputation: float = Field(alias="effectiveReputation")

    model_config = {"populate_by_name": True}


# ──────────────────────────────────────────────
# Guild Models
# ──────────────────────────────────────────────


class GuildMember(BaseModel):
    agent_id: str = Field(alias="agentId")
    role: str
    joined_at: datetime = Field(alias="joinedAt")

    model_config = {"populate_by_name": True}


class Guild(BaseModel):
    id: str
    name: str
    description: str
    domain: str
    owner_agent_id: str = Field(alias="ownerAgentId")
    member_count: int = Field(alias="memberCount")
    metadata: dict[str, Any] = Field(default_factory=dict)
    created_at: datetime = Field(alias="createdAt")
    updated_at: datetime = Field(alias="updatedAt")

    model_config = {"populate_by_name": True}


class GuildWithMembers(Guild):
    members: list[GuildMember] = Field(default_factory=list)


class TopMember(BaseModel):
    agent_id: str = Field(alias="agentId")
    reputation: float
    transaction_count: int = Field(alias="transactionCount")

    model_config = {"populate_by_name": True}


class AnalyticsPeriod(BaseModel):
    from_time: datetime = Field(alias="from")
    to_time: datetime = Field(alias="to")

    model_config = {"populate_by_name": True}


class GuildAnalytics(BaseModel):
    guild_id: str = Field(alias="guildId")
    member_count: int = Field(alias="memberCount")
    average_reputation: float = Field(alias="averageReputation")
    total_transactions: int = Field(alias="totalTransactions")
    total_volume: str = Field(alias="totalVolume")
    top_members: list[TopMember] = Field(alias="topMembers")
    period: AnalyticsPeriod

    model_config = {"populate_by_name": True}


class LeaderboardEntry(BaseModel):
    rank: int
    guild_id: str = Field(alias="guildId")
    name: str
    average_reputation: float = Field(alias="averageReputation")
    member_count: int = Field(alias="memberCount")
    total_volume: str = Field(alias="totalVolume")

    model_config = {"populate_by_name": True}


class GuildLeaderboard(BaseModel):
    entries: list[LeaderboardEntry]
    updated_at: datetime = Field(alias="updatedAt")

    model_config = {"populate_by_name": True}


# ──────────────────────────────────────────────
# Webhook Models
# ──────────────────────────────────────────────


class Webhook(BaseModel):
    id: str
    url: str
    events: list[WebhookEvent]
    secret: str
    active: bool
    created_at: datetime = Field(alias="createdAt")
    updated_at: datetime = Field(alias="updatedAt")

    model_config = {"populate_by_name": True}


# ──────────────────────────────────────────────
# System Models
# ──────────────────────────────────────────────


class ServiceStatus(BaseModel):
    status: str
    latency: float


class HealthStatus(BaseModel):
    status: str
    version: str
    uptime: float
    services: dict[str, ServiceStatus]


class SystemStats(BaseModel):
    total_agents: int = Field(alias="totalAgents")
    active_agents: int = Field(alias="activeAgents")
    total_transactions: int = Field(alias="totalTransactions")
    total_volume: str = Field(alias="totalVolume")
    total_guilds: int = Field(alias="totalGuilds")
    average_reputation: float = Field(alias="averageReputation")
    updated_at: datetime = Field(alias="updatedAt")

    model_config = {"populate_by_name": True}


class DomainInfo(BaseModel):
    name: str
    description: str
    agent_count: int = Field(alias="agentCount")
    transaction_count: int = Field(alias="transactionCount")
    average_reputation: float = Field(alias="averageReputation")

    model_config = {"populate_by_name": True}


class MerkleRootInfo(BaseModel):
    root: str
    block_number: int = Field(alias="blockNumber")
    timestamp: datetime
    agent_count: int = Field(alias="agentCount")

    model_config = {"populate_by_name": True}


class MerkleProofResponse(BaseModel):
    agent_id: str = Field(alias="agentId")
    score: float
    proof: list[str]
    root: str
    leaf: str

    model_config = {"populate_by_name": True}
