# Copyright 2026 The Gitea Authors. All rights reserved.
# SPDX-License-Identifier: MIT

"""Data models for the Gitea SDK."""

from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional, List, Dict, Any


@dataclass
class User:
    """Represents a Gitea user."""
    id: int
    login: str
    full_name: str = ""
    email: str = ""
    avatar_url: str = ""
    is_admin: bool = False

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "User":
        return cls(
            id=data.get("id", 0),
            login=data.get("login", ""),
            full_name=data.get("full_name", ""),
            email=data.get("email", ""),
            avatar_url=data.get("avatar_url", ""),
            is_admin=data.get("is_admin", False),
        )


@dataclass
class Repository:
    """Represents a Gitea repository."""
    id: int
    name: str
    full_name: str
    owner: Optional[User] = None
    description: str = ""
    private: bool = False
    fork: bool = False
    default_branch: str = "main"
    stars_count: int = 0
    forks_count: int = 0
    clone_url: str = ""
    html_url: str = ""

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Repository":
        owner = None
        if data.get("owner"):
            owner = User.from_dict(data["owner"])
        return cls(
            id=data.get("id", 0),
            name=data.get("name", ""),
            full_name=data.get("full_name", ""),
            owner=owner,
            description=data.get("description", ""),
            private=data.get("private", False),
            fork=data.get("fork", False),
            default_branch=data.get("default_branch", "main"),
            stars_count=data.get("stars_count", 0),
            forks_count=data.get("forks_count", 0),
            clone_url=data.get("clone_url", ""),
            html_url=data.get("html_url", ""),
        )


@dataclass
class Attachment:
    """Represents a release attachment/asset."""
    id: int
    name: str
    size: int
    download_count: int = 0
    download_url: str = ""
    created_at: Optional[datetime] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Attachment":
        created_at = None
        if data.get("created_at"):
            try:
                created_at = datetime.fromisoformat(data["created_at"].replace("Z", "+00:00"))
            except (ValueError, AttributeError):
                pass
        return cls(
            id=data.get("id", 0),
            name=data.get("name", ""),
            size=data.get("size", 0),
            download_count=data.get("download_count", 0),
            download_url=data.get("browser_download_url", ""),
            created_at=created_at,
        )


@dataclass
class Release:
    """Represents a Gitea release."""
    id: int
    tag_name: str
    name: str = ""
    body: str = ""
    draft: bool = False
    prerelease: bool = False
    published_at: Optional[datetime] = None
    assets: List[Attachment] = field(default_factory=list)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Release":
        published_at = None
        if data.get("published_at"):
            try:
                published_at = datetime.fromisoformat(data["published_at"].replace("Z", "+00:00"))
            except (ValueError, AttributeError):
                pass

        assets = []
        for asset_data in data.get("assets", []):
            assets.append(Attachment.from_dict(asset_data))

        return cls(
            id=data.get("id", 0),
            tag_name=data.get("tag_name", ""),
            name=data.get("name", ""),
            body=data.get("body", ""),
            draft=data.get("draft", False),
            prerelease=data.get("prerelease", False),
            published_at=published_at,
            assets=assets,
        )


@dataclass
class UploadSession:
    """Represents a chunked upload session."""
    id: str
    file_name: str
    file_size: int
    chunk_size: int
    total_chunks: int
    chunks_received: int = 0
    status: str = "pending"
    expires_at: Optional[datetime] = None
    checksum: str = ""

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "UploadSession":
        expires_at = None
        if data.get("expires_at"):
            try:
                expires_at = datetime.fromisoformat(data["expires_at"].replace("Z", "+00:00"))
            except (ValueError, AttributeError):
                pass
        return cls(
            id=data.get("id", ""),
            file_name=data.get("file_name", ""),
            file_size=data.get("file_size", 0),
            chunk_size=data.get("chunk_size", 0),
            total_chunks=data.get("total_chunks", 0),
            chunks_received=data.get("chunks_received", 0),
            status=data.get("status", "pending"),
            expires_at=expires_at,
            checksum=data.get("checksum", ""),
        )


@dataclass
class UploadResult:
    """Represents the result of a completed upload."""
    id: int
    name: str
    size: int
    download_url: str
    checksum_verified: bool = False

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "UploadResult":
        return cls(
            id=data.get("id", 0),
            name=data.get("name", ""),
            size=data.get("size", 0),
            download_url=data.get("browser_download_url", ""),
            checksum_verified=data.get("checksum_verified", False),
        )


@dataclass
class Progress:
    """Represents upload progress."""
    bytes_done: int
    bytes_total: int
    chunks_done: int
    chunks_total: int
    percent: float
    speed: float  # bytes per second
    eta_seconds: float

    @property
    def eta(self) -> str:
        """Format ETA as a human-readable string."""
        seconds = int(self.eta_seconds)
        if seconds < 60:
            return f"{seconds}s"
        minutes = seconds // 60
        seconds = seconds % 60
        if minutes < 60:
            return f"{minutes}m{seconds}s"
        hours = minutes // 60
        minutes = minutes % 60
        return f"{hours}h{minutes}m"

    @property
    def speed_formatted(self) -> str:
        """Format speed as a human-readable string."""
        if self.speed < 1024:
            return f"{self.speed:.0f} B/s"
        elif self.speed < 1024 * 1024:
            return f"{self.speed / 1024:.1f} KB/s"
        else:
            return f"{self.speed / 1024 / 1024:.1f} MB/s"
