from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any, Optional


BTC_PREFIX = "btc-updown-5m-"
SESSION_SEC = 300
ET_TZ_NAME = "America/New_York"


def normalize_addr(value: Optional[str]) -> str:
    if not value:
        return ""
    return value.lower()


def parse_dt(value: Any) -> Optional[datetime]:
    if value in (None, ""):
        return None
    if isinstance(value, datetime):
        if value.tzinfo is None:
            return value.replace(tzinfo=timezone.utc)
        return value.astimezone(timezone.utc)
    if isinstance(value, (int, float)):
        return datetime.fromtimestamp(float(value), tz=timezone.utc)
    if isinstance(value, str):
        try:
            return datetime.fromisoformat(value.replace("Z", "+00:00")).astimezone(
                timezone.utc
            )
        except ValueError:
            try:
                return datetime.fromtimestamp(float(value), tz=timezone.utc)
            except (TypeError, ValueError, OSError, OverflowError):
                return None
    return None


def dt_to_iso(value: Optional[datetime]) -> str:
    if value is None:
        return ""
    if value.tzinfo is None:
        value = value.replace(tzinfo=timezone.utc)
    return value.astimezone(timezone.utc).isoformat()


def float_or_none(value: Any) -> Optional[float]:
    if value in (None, ""):
        return None
    try:
        return float(value)
    except (TypeError, ValueError):
        return None


def int_or_none(value: Any) -> Optional[int]:
    if value in (None, ""):
        return None
    try:
        return int(value)
    except (TypeError, ValueError):
        return None


def slug_start_ts(slug: str) -> Optional[int]:
    if not slug.startswith(BTC_PREFIX):
        return None
    return int_or_none(slug[len(BTC_PREFIX) :])


def slug_for_ts(timestamp: int) -> str:
    return f"{BTC_PREFIX}{(int(timestamp) // SESSION_SEC) * SESSION_SEC}"


def floor_session_ts(timestamp: int) -> int:
    return (int(timestamp) // SESSION_SEC) * SESSION_SEC


@dataclass
class MarketInfo:
    slug: str
    condition_id: str = ""
    title: str = ""
    event_slug: str = ""
    start_time: Optional[datetime] = None
    end_time: Optional[datetime] = None
    up_token_id: str = ""
    down_token_id: str = ""
    resolved: bool = False
    winning_outcome: str = ""
    final_price: Optional[float] = None
    price_to_beat: Optional[float] = None
    source: str = ""

    def to_row(self) -> dict[str, Any]:
        return {
            "market_slug": self.slug,
            "event_slug": self.event_slug,
            "condition_id": self.condition_id,
            "question": self.title,
            "start_time": dt_to_iso(self.start_time),
            "end_time": dt_to_iso(self.end_time),
            "up_token_id": self.up_token_id,
            "down_token_id": self.down_token_id,
            "resolved": str(bool(self.resolved)).lower(),
            "winning_outcome": self.winning_outcome,
            "final_price": "" if self.final_price is None else self.final_price,
            "price_to_beat": "" if self.price_to_beat is None else self.price_to_beat,
            "source": self.source,
        }

    @classmethod
    def from_row(cls, row: dict[str, Any]) -> "MarketInfo":
        return cls(
            slug=row.get("market_slug") or row.get("slug") or "",
            event_slug=row.get("event_slug", ""),
            condition_id=row.get("condition_id", ""),
            title=row.get("question", ""),
            start_time=parse_dt(row.get("start_time")),
            end_time=parse_dt(row.get("end_time")),
            up_token_id=row.get("up_token_id", ""),
            down_token_id=row.get("down_token_id", ""),
            resolved=str(row.get("resolved", "")).lower() == "true",
            winning_outcome=row.get("winning_outcome", ""),
            final_price=float_or_none(row.get("final_price")),
            price_to_beat=float_or_none(row.get("price_to_beat")),
            source=row.get("source", ""),
        )


@dataclass
class SafeAction:
    safe: str
    market_slug: str
    condition_id: str
    tx_hash: str
    timestamp: datetime
    action_type: str
    source: str
    raw_type: str = ""
    outcome: str = ""
    side: str = ""
    price: Optional[float] = None
    shares: float = 0.0
    gross_usdc: float = 0.0
    net_usdc_delta: Optional[float] = None
    usdc_delta: float = 0.0
    up_delta: float = 0.0
    down_delta: float = 0.0
    maker_taker_role: str = "not_trade"
    counterparties: str = ""
    pnl_eligible: bool = True

    @property
    def action_id(self) -> str:
        parts = [
            self.tx_hash.lower(),
            self.action_type,
            self.side,
            self.outcome,
            str(int(self.timestamp.timestamp())),
            f"{self.shares:.8f}",
            f"{self.gross_usdc:.8f}",
        ]
        return "|".join(parts)

    def to_row(self) -> dict[str, Any]:
        return {
            "action_id": self.action_id,
            "safe": self.safe,
            "market_slug": self.market_slug,
            "condition_id": self.condition_id,
            "tx_hash": self.tx_hash,
            "timestamp": dt_to_iso(self.timestamp),
            "action_type": self.action_type,
            "source": self.source,
            "raw_type": self.raw_type,
            "outcome": self.outcome,
            "side": self.side,
            "price": "" if self.price is None else self.price,
            "shares": self.shares,
            "gross_usdc": self.gross_usdc,
            "net_usdc_delta": ""
            if self.net_usdc_delta is None
            else self.net_usdc_delta,
            "usdc_delta": self.usdc_delta,
            "up_delta": self.up_delta,
            "down_delta": self.down_delta,
            "maker_taker_role": self.maker_taker_role,
            "counterparties": self.counterparties,
            "pnl_eligible": str(bool(self.pnl_eligible)).lower(),
        }

    @classmethod
    def from_row(cls, row: dict[str, Any]) -> "SafeAction":
        ts = parse_dt(row.get("timestamp")) or datetime.fromtimestamp(0, tz=timezone.utc)
        return cls(
            safe=row.get("safe", ""),
            market_slug=row.get("market_slug", ""),
            condition_id=row.get("condition_id", ""),
            tx_hash=row.get("tx_hash", ""),
            timestamp=ts,
            action_type=row.get("action_type", ""),
            source=row.get("source", ""),
            raw_type=row.get("raw_type", ""),
            outcome=row.get("outcome", ""),
            side=row.get("side", ""),
            price=float_or_none(row.get("price")),
            shares=float_or_none(row.get("shares")) or 0.0,
            gross_usdc=float_or_none(row.get("gross_usdc")) or 0.0,
            net_usdc_delta=float_or_none(row.get("net_usdc_delta")),
            usdc_delta=float_or_none(row.get("usdc_delta")) or 0.0,
            up_delta=float_or_none(row.get("up_delta")) or 0.0,
            down_delta=float_or_none(row.get("down_delta")) or 0.0,
            maker_taker_role=row.get("maker_taker_role", "not_trade"),
            counterparties=row.get("counterparties", ""),
            pnl_eligible=str(row.get("pnl_eligible", "true")).lower() == "true",
        )


@dataclass
class ReceiptSummary:
    tx_hash: str
    status: str = ""
    block_number: Optional[int] = None
    tx_from: str = ""
    tx_to: str = ""
    gas_fee_pol: Optional[float] = None
    safe_usdc_net_delta: float = 0.0
    maker_taker_role: str = "unknown"
    counterparties: tuple[str, ...] = ()
    real_fill_count: int = 0
    summary_fill_count: int = 0

