# retoor <retoor@molodetz.nl>

from datetime import datetime, timezone

from devplacepy.services.gitea.client import (
    STATE_ALL,
    STATE_CLOSED,
    STATE_OPEN,
    GiteaError,
)


def _now() -> str:
    return datetime.now(timezone.utc).isoformat()


class FakeGiteaClient:
    def __init__(self, bot_login: str = "devplace-bot"):
        self.bot_login = bot_login
        self._issues: dict[int, dict] = {}
        self._comments: dict[int, list[dict]] = {}
        self._issue_assets: dict[int, list[dict]] = {}
        self._comment_assets: dict[int, list[dict]] = {}
        self._issue_seq = 0
        self._comment_seq = 0
        self._asset_seq = 0

    def _html(self, number: int) -> str:
        return f"https://gitea.test/retoor/pydevplace/issues/{number}"

    async def create_issue(self, title: str, body: str) -> dict:
        self._issue_seq += 1
        number = self._issue_seq
        now = _now()
        issue = {
            "number": number,
            "title": title,
            "body": body,
            "state": STATE_OPEN,
            "html_url": self._html(number),
            "user": {"login": self.bot_login},
            "comments": 0,
            "created_at": now,
            "updated_at": now,
            "closed_at": None,
        }
        self._issues[number] = issue
        self._comments[number] = []
        return dict(issue)

    async def list_issues(
        self, state: str = STATE_OPEN, page: int = 1, limit: int = 20
    ) -> tuple[list[dict], int]:
        rows = [
            issue
            for issue in self._issues.values()
            if state == STATE_ALL or issue["state"] == state
        ]
        rows.sort(key=lambda item: item["number"], reverse=True)
        total = len(rows)
        start = (max(1, page) - 1) * limit
        return [dict(item) for item in rows[start : start + limit]], total

    async def get_issue(self, number: int) -> dict:
        issue = self._issues.get(number)
        if not issue:
            raise GiteaError("issue not found", status=404)
        return dict(issue)

    async def list_comments(self, number: int) -> list[dict]:
        if number not in self._issues:
            raise GiteaError("issue not found", status=404)
        return [dict(item) for item in self._comments.get(number, [])]

    async def create_comment(self, number: int, body: str) -> dict:
        if number not in self._issues:
            raise GiteaError("issue not found", status=404)
        self._comment_seq += 1
        now = _now()
        comment = {
            "id": self._comment_seq,
            "body": body,
            "html_url": f"{self._html(number)}#comment-{self._comment_seq}",
            "user": {"login": self.bot_login},
            "created_at": now,
            "updated_at": now,
        }
        self._comments[number].append(comment)
        self._issues[number]["comments"] = len(self._comments[number])
        return dict(comment)

    async def set_state(self, number: int, state: str) -> dict:
        issue = self._issues.get(number)
        if not issue:
            raise GiteaError("issue not found", status=404)
        if state not in (STATE_OPEN, STATE_CLOSED):
            raise GiteaError(f"invalid state: {state}", status=400)
        issue["state"] = state
        issue["closed_at"] = _now() if state == STATE_CLOSED else None
        issue["updated_at"] = _now()
        return dict(issue)

    def _asset(self, filename: str, data: bytes) -> dict:
        self._asset_seq += 1
        return {
            "id": self._asset_seq,
            "name": filename,
            "size": len(data),
            "download_count": 0,
            "uuid": f"asset-{self._asset_seq}",
            "browser_download_url": f"https://gitea.test/attachments/asset-{self._asset_seq}",
            "created_at": _now(),
        }

    async def list_issue_assets(self, number: int) -> list[dict]:
        if number not in self._issues:
            raise GiteaError("issue not found", status=404)
        return [dict(item) for item in self._issue_assets.get(number, [])]

    async def create_issue_asset(
        self, number: int, filename: str, data: bytes, mime: str
    ) -> dict:
        if number not in self._issues:
            raise GiteaError("issue not found", status=404)
        asset = self._asset(filename, data)
        self._issue_assets.setdefault(number, []).append(asset)
        return dict(asset)

    async def delete_issue_asset(self, number: int, attachment_id: int) -> None:
        assets = self._issue_assets.get(number, [])
        self._issue_assets[number] = [a for a in assets if a["id"] != attachment_id]

    async def list_comment_assets(self, comment_id: int) -> list[dict]:
        return [dict(item) for item in self._comment_assets.get(comment_id, [])]

    async def create_comment_asset(
        self, comment_id: int, filename: str, data: bytes, mime: str
    ) -> dict:
        asset = self._asset(filename, data)
        self._comment_assets.setdefault(comment_id, []).append(asset)
        return dict(asset)

    async def delete_comment_asset(self, comment_id: int, attachment_id: int) -> None:
        assets = self._comment_assets.get(comment_id, [])
        self._comment_assets[comment_id] = [
            a for a in assets if a["id"] != attachment_id
        ]

    def add_external_comment(self, number: int, login: str, body: str) -> dict:
        self._comment_seq += 1
        now = _now()
        comment = {
            "id": self._comment_seq,
            "body": body,
            "html_url": f"{self._html(number)}#comment-{self._comment_seq}",
            "user": {"login": login},
            "created_at": now,
            "updated_at": now,
        }
        self._comments.setdefault(number, []).append(comment)
        if number in self._issues:
            self._issues[number]["comments"] = len(self._comments[number])
        return dict(comment)
