from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Literal, Optional, TypedDict


@dataclass(frozen=True)
class IngestSource:
    uri: str
    kind: Literal["url", "file"] = "url"
    label: Optional[str] = None

    def to_dict(self) -> dict:
        data = {"uri": self.uri, "kind": self.kind}
        if self.label:
            data["label"] = self.label
        return data


@dataclass(frozen=True)
class UploadResult:
    obj_ulid: str
    uri: str
    filename: str
    size_bytes: int

    def to_ingest_source(self, label: Optional[str] = None) -> IngestSource:
        return IngestSource(uri=self.uri, kind="file", label=label)


class MarketplaceInputContractField(TypedDict, total=False):
    id: str
    label: str
    type: str
    required: bool
    max_length: int | None
    enum: list[str] | None


class MarketplaceInputContract(TypedDict):
    fields: list[MarketplaceInputContractField]


class MarketplaceRecipeSummary(TypedDict, total=False):
    recipe_ulid: str
    title: str
    description: str | None
    visibility: str
    listed: bool
    is_composable: bool
    revision_sha: str
    published_at: str
    total_runs: int
    success_rate: float | None
    avg_credits_per_run: float | None
    avg_duration_ms: float | None
    avg_rating: float | None
    rating_count: int
    safety_status: str
    marketplace: dict[str, Any]


class MarketplaceRecipeDetail(MarketplaceRecipeSummary, total=False):
    contract_preview: dict[str, Any]
    input_contract: MarketplaceInputContract


class MarketplaceRecipeSearchItem(MarketplaceRecipeSummary, total=False):
    score: float


class MarketplaceListResponse(TypedDict):
    count: int
    next: str | None
    previous: str | None
    results: list[MarketplaceRecipeSummary]


class MarketplaceSearchResponse(TypedDict):
    count: int
    next: str | None
    previous: str | None
    results: list[MarketplaceRecipeSearchItem]
