"""Arena API client for competition submissions."""

from __future__ import annotations

import time
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING, IO, Any

from botmanifold.types import (
    JudgeVerdict,
    LeaderboardEntry,
    Scenario,
    Submission,
    SubmissionStatus,
)

if TYPE_CHECKING:
    from botmanifold.client import BotManifoldClient


class ArenaClient:
    """Client for Bot Arena competition.

    Use this to submit policies to the arena, check submission status,
    and view leaderboard standings.

    Example:
        >>> client = BotManifoldClient(api_key="...")
        >>> submission = client.arena.submit("my_policy.zip", scenario="messy_room")
        >>> print(f"Submitted: {submission.id}")
    """

    def __init__(self, parent: "BotManifoldClient") -> None:
        self._parent = parent

    def submit(
        self,
        policy: str | Path | IO[bytes],
        scenario: str,
        description: str | None = None,
    ) -> Submission:
        """Submit a policy to the arena competition.

        Args:
            policy: Path to policy zip file, or file-like object.
            scenario: Scenario ID to submit to.
            description: Optional description for this submission.

        Returns:
            Submission object with ID and initial status.
        """
        if isinstance(policy, (str, Path)):
            path = Path(policy)
            if not path.exists():
                raise FileNotFoundError(f"File not found: {path}")
            if not path.suffix == ".zip":
                raise ValueError("Policy must be a .zip file")
            if path.stat().st_size > 10 * 1024 * 1024:  # 10MB
                raise ValueError("Policy file exceeds 10MB limit")

            with open(path, "rb") as f:
                return self._submit_file(f, path.name, scenario, description)
        else:
            return self._submit_file(policy, "policy.zip", scenario, description)

    def _submit_file(
        self,
        file: IO[bytes],
        filename: str,
        scenario: str,
        description: str | None,
    ) -> Submission:
        """Submit a file to the arena."""
        files = {"file": (filename, file, "application/zip")}
        params = {"scenario": scenario}
        if description:
            params["description"] = description

        data = self._parent._request("POST", "/submissions", files=files, params=params)

        return Submission(
            id=data["submission_id"],
            scenario_id=data["scenario_id"],
            status=SubmissionStatus(data["status"]),
            created_at=datetime.fromisoformat(
                data["created_at"].replace("Z", "+00:00")
            ),
        )

    def status(self, submission_id: str) -> Submission:
        """Get the status of a submission.

        Args:
            submission_id: The submission ID from submit().

        Returns:
            Submission object with current status.
        """
        data = self._parent._request("GET", f"/submissions/{submission_id}")

        verdict = None
        confidence = None
        try:
            judge_data = self._parent._request("GET", f"/submissions/{submission_id}/judge")
            verdict = JudgeVerdict(judge_data["verdict"])
            confidence = judge_data.get("confidence")
        except Exception:
            pass

        return Submission(
            id=data["id"],
            scenario_id=data["scenario_id"],
            status=SubmissionStatus(data["status"]),
            created_at=datetime.fromisoformat(
                data["created_at"].replace("Z", "+00:00")
            ),
            verdict=verdict,
            confidence=confidence,
            video_url=data.get("video_url"),
            error_message=data.get("error_message"),
        )

    def wait(
        self,
        submission_id: str,
        timeout: int = 300,
        poll_interval: int = 5,
    ) -> Submission:
        """Wait for a submission to complete.

        Args:
            submission_id: The submission ID to wait for.
            timeout: Maximum time to wait in seconds.
            poll_interval: Time between status checks.

        Returns:
            Submission with final status.
        """
        start_time = time.time()
        while time.time() - start_time < timeout:
            submission = self.status(submission_id)
            if submission.status in (SubmissionStatus.COMPLETED, SubmissionStatus.FAILED):
                return submission
            time.sleep(poll_interval)

        raise TimeoutError(
            f"Submission {submission_id} did not complete within {timeout}s"
        )

    def leaderboard(
        self,
        scenario: str | None = None,
        limit: int = 50,
        offset: int = 0,
    ) -> list[LeaderboardEntry]:
        """Get the arena leaderboard.

        Args:
            scenario: Filter by scenario ID (optional).
            limit: Maximum entries to return.
            offset: Number of entries to skip.

        Returns:
            List of LeaderboardEntry objects.
        """
        params: dict[str, Any] = {"limit": limit, "offset": offset}
        if scenario:
            params["scenario"] = scenario

        data = self._parent._request("GET", "/submissions", params=params)

        entries = []
        for i, sub in enumerate(data["submissions"], start=offset + 1):
            entries.append(
                LeaderboardEntry(
                    rank=i,
                    submission_id=sub["id"],
                    user_id=sub.get("user_id", "anonymous"),
                    scenario_id=sub["scenario_id"],
                    score=sub.get("score", 0.0),
                    verdict=(
                        JudgeVerdict(sub["verdict"])
                        if sub.get("verdict")
                        else JudgeVerdict.PASS
                    ),
                    submitted_at=datetime.fromisoformat(
                        sub["created_at"].replace("Z", "+00:00")
                    ),
                )
            )

        return entries

    def list_scenarios(self) -> list[Scenario]:
        """List available arena scenarios.

        Returns:
            List of Scenario objects.
        """
        return [
            Scenario(
                id="messy-room",
                name="Messy Room Challenge",
                description="Clean up a messy room by moving objects to targets.",
                difficulty=2,
                time_limit=30.0,
                observation_space={
                    "type": "dict",
                    "robot_state": "array(7,)",
                    "objects": "dict",
                },
                action_space={"type": "array", "shape": (3,), "low": -1.0, "high": 1.0},
            ),
        ]
