# Code generated by python/tools/generate_jira_rest_models.py. DO NOT EDIT.
from __future__ import annotations

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

from atlassian.errors import SerializationError

def _expect_dict(obj: Any, path: str) -> Dict[str, Any]:
    if not isinstance(obj, dict):
        raise SerializationError(f"Expected object at {path}")
    return obj


def _expect_list(obj: Any, path: str) -> List[Any]:
    if not isinstance(obj, list):
        raise SerializationError(f"Expected list at {path}")
    return obj


def _expect_str(obj: Any, path: str) -> str:
    if not isinstance(obj, str):
        raise SerializationError(f"Expected string at {path}")
    return obj


def _expect_bool(obj: Any, path: str) -> bool:
    if not isinstance(obj, bool):
        raise SerializationError(f"Expected boolean at {path}")
    return obj


def _expect_int(obj: Any, path: str) -> int:
    if isinstance(obj, bool) or not isinstance(obj, int):
        raise SerializationError(f"Expected integer at {path}")
    return obj


def _expect_obj(obj: Any, path: str) -> Dict[str, Any]:
    # Jira issue fields are modeled as a free-form object in the OpenAPI spec.
    return _expect_dict(obj, path)

@dataclass(frozen=True)
class UserDetails:
    account_id: Optional[str]
    display_name: Optional[str]
    email_address: Optional[str] = None

    @staticmethod
    def from_dict(obj: Any, path: str) -> "UserDetails":
        raw = _expect_dict(obj, path)
        account_id: Optional[str] = None
        if raw.get("accountId") is not None:
            account_id = _expect_str(raw.get("accountId"), f"{path}.accountId")
        display_name: Optional[str] = None
        if raw.get("displayName") is not None:
            display_name = _expect_str(raw.get("displayName"), f"{path}.displayName")
        email_address: Optional[str] = None
        if raw.get("emailAddress") is not None:
            email_address = _expect_str(raw.get("emailAddress"), f"{path}.emailAddress")
        return UserDetails(
            account_id=account_id,
            display_name=display_name,
            email_address=email_address,
        )


@dataclass(frozen=True)
class Project:
    id: Optional[str]
    key: Optional[str]
    name: Optional[str]
    project_type_key: Optional[str] = None

    @staticmethod
    def from_dict(obj: Any, path: str) -> "Project":
        raw = _expect_dict(obj, path)
        project_id: Optional[str] = None
        if raw.get("id") is not None:
            project_id = _expect_str(raw.get("id"), f"{path}.id")
        key: Optional[str] = None
        if raw.get("key") is not None:
            key = _expect_str(raw.get("key"), f"{path}.key")
        name: Optional[str] = None
        if raw.get("name") is not None:
            name = _expect_str(raw.get("name"), f"{path}.name")
        project_type_key: Optional[str] = None
        if raw.get("projectTypeKey") is not None:
            project_type_key = _expect_str(raw.get("projectTypeKey"), f"{path}.projectTypeKey")
        return Project(
            id=project_id,
            key=key,
            name=name,
            project_type_key=project_type_key,
        )


@dataclass(frozen=True)
class PageBeanProject:
    start_at: Optional[int]
    max_results: Optional[int]
    total: Optional[int]
    is_last: Optional[bool]
    values: List[Project]

    @staticmethod
    def from_dict(obj: Any, path: str) -> "PageBeanProject":
        raw = _expect_dict(obj, path)
        start_at: Optional[int] = None
        if raw.get("startAt") is not None:
            start_at = _expect_int(raw.get("startAt"), f"{path}.startAt")
        max_results: Optional[int] = None
        if raw.get("maxResults") is not None:
            max_results = _expect_int(raw.get("maxResults"), f"{path}.maxResults")
        total: Optional[int] = None
        if raw.get("total") is not None:
            total = _expect_int(raw.get("total"), f"{path}.total")
        is_last: Optional[bool] = None
        if raw.get("isLast") is not None:
            is_last = _expect_bool(raw.get("isLast"), f"{path}.isLast")
        values_raw = raw.get("values")
        values_list = _expect_list(values_raw, f"{path}.values") if values_raw is not None else []
        values = [
            Project.from_dict(item, f"{path}.values[{idx}]")
            for idx, item in enumerate(values_list)
        ]
        return PageBeanProject(
            start_at=start_at,
            max_results=max_results,
            total=total,
            is_last=is_last,
            values=values,
        )


@dataclass(frozen=True)
class IssueBean:
    id: Optional[str]
    key: Optional[str]
    fields: Dict[str, Any]

    @staticmethod
    def from_dict(obj: Any, path: str) -> "IssueBean":
        raw = _expect_dict(obj, path)
        issue_id: Optional[str] = None
        if raw.get("id") is not None:
            issue_id = _expect_str(raw.get("id"), f"{path}.id")
        key: Optional[str] = None
        if raw.get("key") is not None:
            key = _expect_str(raw.get("key"), f"{path}.key")
        fields_raw = raw.get("fields")
        fields = _expect_obj(fields_raw, f"{path}.fields") if fields_raw is not None else {}
        return IssueBean(id=issue_id, key=key, fields=fields)


@dataclass(frozen=True)
class SearchResults:
    start_at: Optional[int]
    max_results: Optional[int]
    total: Optional[int]
    issues: List[IssueBean]

    @staticmethod
    def from_dict(obj: Any, path: str) -> "SearchResults":
        raw = _expect_dict(obj, path)
        start_at: Optional[int] = None
        if raw.get("startAt") is not None:
            start_at = _expect_int(raw.get("startAt"), f"{path}.startAt")
        max_results: Optional[int] = None
        if raw.get("maxResults") is not None:
            max_results = _expect_int(raw.get("maxResults"), f"{path}.maxResults")
        total: Optional[int] = None
        if raw.get("total") is not None:
            total = _expect_int(raw.get("total"), f"{path}.total")
        issues_raw = raw.get("issues")
        issues_list = _expect_list(issues_raw, f"{path}.issues") if issues_raw is not None else []
        issues = [
            IssueBean.from_dict(item, f"{path}.issues[{idx}]")
            for idx, item in enumerate(issues_list)
        ]
        return SearchResults(
            start_at=start_at,
            max_results=max_results,
            total=total,
            issues=issues,
        )


@dataclass(frozen=True)
class ChangeDetails:
    field: Optional[str]
    from_value: Optional[str] = None
    to_value: Optional[str] = None
    from_string: Optional[str] = None
    to_string: Optional[str] = None

    @staticmethod
    def from_dict(obj: Any, path: str) -> "ChangeDetails":
        raw = _expect_dict(obj, path)
        field: Optional[str] = None
        if raw.get("field") is not None:
            field = _expect_str(raw.get("field"), f"{path}.field")
        from_value: Optional[str] = None
        if raw.get("from") is not None:
            from_value = _expect_str(raw.get("from"), f"{path}.from")
        to_value: Optional[str] = None
        if raw.get("to") is not None:
            to_value = _expect_str(raw.get("to"), f"{path}.to")
        from_string: Optional[str] = None
        if raw.get("fromString") is not None:
            from_string = _expect_str(raw.get("fromString"), f"{path}.fromString")
        to_string: Optional[str] = None
        if raw.get("toString") is not None:
            to_string = _expect_str(raw.get("toString"), f"{path}.toString")
        return ChangeDetails(
            field=field,
            from_value=from_value,
            to_value=to_value,
            from_string=from_string,
            to_string=to_string,
        )


@dataclass(frozen=True)
class Changelog:
    id: Optional[str]
    created: Optional[str]
    items: List[ChangeDetails]
    author: Optional[UserDetails] = None

    @staticmethod
    def from_dict(obj: Any, path: str) -> "Changelog":
        raw = _expect_dict(obj, path)
        event_id: Optional[str] = None
        if raw.get("id") is not None:
            event_id = _expect_str(raw.get("id"), f"{path}.id")
        created: Optional[str] = None
        if raw.get("created") is not None:
            created = _expect_str(raw.get("created"), f"{path}.created")
        author: Optional[UserDetails] = None
        if raw.get("author") is not None:
            author = UserDetails.from_dict(raw.get("author"), f"{path}.author")
        items_raw = raw.get("items")
        items_list = _expect_list(items_raw, f"{path}.items") if items_raw is not None else []
        items = [
            ChangeDetails.from_dict(item, f"{path}.items[{idx}]")
            for idx, item in enumerate(items_list)
        ]
        return Changelog(
            id=event_id,
            created=created,
            items=items,
            author=author,
        )


@dataclass(frozen=True)
class PageBeanChangelog:
    start_at: Optional[int]
    max_results: Optional[int]
    total: Optional[int]
    is_last: Optional[bool]
    values: List[Changelog]

    @staticmethod
    def from_dict(obj: Any, path: str) -> "PageBeanChangelog":
        raw = _expect_dict(obj, path)
        start_at: Optional[int] = None
        if raw.get("startAt") is not None:
            start_at = _expect_int(raw.get("startAt"), f"{path}.startAt")
        max_results: Optional[int] = None
        if raw.get("maxResults") is not None:
            max_results = _expect_int(raw.get("maxResults"), f"{path}.maxResults")
        total: Optional[int] = None
        if raw.get("total") is not None:
            total = _expect_int(raw.get("total"), f"{path}.total")
        is_last: Optional[bool] = None
        if raw.get("isLast") is not None:
            is_last = _expect_bool(raw.get("isLast"), f"{path}.isLast")
        values_raw = raw.get("values")
        values_list = _expect_list(values_raw, f"{path}.values") if values_raw is not None else []
        values = [
            Changelog.from_dict(item, f"{path}.values[{idx}]")
            for idx, item in enumerate(values_list)
        ]
        return PageBeanChangelog(
            start_at=start_at,
            max_results=max_results,
            total=total,
            is_last=is_last,
            values=values,
        )


@dataclass(frozen=True)
class Worklog:
    id: Optional[str]
    started: Optional[str]
    time_spent_seconds: Optional[int]
    created: Optional[str]
    updated: Optional[str]
    author: Optional[UserDetails] = None

    @staticmethod
    def from_dict(obj: Any, path: str) -> "Worklog":
        raw = _expect_dict(obj, path)
        worklog_id: Optional[str] = None
        if raw.get("id") is not None:
            worklog_id = _expect_str(raw.get("id"), f"{path}.id")
        started: Optional[str] = None
        if raw.get("started") is not None:
            started = _expect_str(raw.get("started"), f"{path}.started")
        time_spent_seconds: Optional[int] = None
        if raw.get("timeSpentSeconds") is not None:
            time_spent_seconds = _expect_int(raw.get("timeSpentSeconds"), f"{path}.timeSpentSeconds")
        created: Optional[str] = None
        if raw.get("created") is not None:
            created = _expect_str(raw.get("created"), f"{path}.created")
        updated: Optional[str] = None
        if raw.get("updated") is not None:
            updated = _expect_str(raw.get("updated"), f"{path}.updated")
        author: Optional[UserDetails] = None
        if raw.get("author") is not None:
            author = UserDetails.from_dict(raw.get("author"), f"{path}.author")
        return Worklog(
            id=worklog_id,
            started=started,
            time_spent_seconds=time_spent_seconds,
            created=created,
            updated=updated,
            author=author,
        )


@dataclass(frozen=True)
class PageOfWorklogs:
    start_at: Optional[int]
    max_results: Optional[int]
    total: Optional[int]
    worklogs: List[Worklog]

    @staticmethod
    def from_dict(obj: Any, path: str) -> "PageOfWorklogs":
        raw = _expect_dict(obj, path)
        start_at: Optional[int] = None
        if raw.get("startAt") is not None:
            start_at = _expect_int(raw.get("startAt"), f"{path}.startAt")
        max_results: Optional[int] = None
        if raw.get("maxResults") is not None:
            max_results = _expect_int(raw.get("maxResults"), f"{path}.maxResults")
        total: Optional[int] = None
        if raw.get("total") is not None:
            total = _expect_int(raw.get("total"), f"{path}.total")
        worklogs_raw = raw.get("worklogs")
        worklogs_list = _expect_list(worklogs_raw, f"{path}.worklogs") if worklogs_raw is not None else []
        worklogs = [
            Worklog.from_dict(item, f"{path}.worklogs[{idx}]")
            for idx, item in enumerate(worklogs_list)
        ]
        return PageOfWorklogs(
            start_at=start_at,
            max_results=max_results,
            total=total,
            worklogs=worklogs,
        )


@dataclass(frozen=True)
class PageBeanVersion:
    start_at: Optional[int]
    max_results: Optional[int]
    total: Optional[int]
    is_last: Optional[bool]
    values: List[Version]

    @staticmethod
    def from_dict(obj: Any, path: str) -> "PageBeanVersion":
        raw = _expect_dict(obj, path)
        start_at: Optional[int] = None
        if raw.get("startAt") is not None:
            start_at = _expect_int(raw.get("startAt"), f"{path}.startAt")
        max_results: Optional[int] = None
        if raw.get("maxResults") is not None:
            max_results = _expect_int(raw.get("maxResults"), f"{path}.maxResults")
        total: Optional[int] = None
        if raw.get("total") is not None:
            total = _expect_int(raw.get("total"), f"{path}.total")
        is_last: Optional[bool] = None
        if raw.get("isLast") is not None:
            is_last = _expect_bool(raw.get("isLast"), f"{path}.isLast")
        values_raw = raw.get("values")
        values_list = _expect_list(values_raw, f"{path}.values") if values_raw is not None else []
        values = [
            Version.from_dict(item, f"{path}.values[{idx}]")
            for idx, item in enumerate(values_list)
        ]
        return PageBeanVersion(
            start_at=start_at,
            max_results=max_results,
            total=total,
            is_last=is_last,
            values=values,
        )


@dataclass(frozen=True)
class Version:
    id: Optional[str]
    name: Optional[str]
    project_id: Optional[int] = None
    released: Optional[bool] = None
    release_date: Optional[str] = None

    @staticmethod
    def from_dict(obj: Any, path: str) -> "Version":
        raw = _expect_dict(obj, path)
        version_id: Optional[str] = None
        if raw.get("id") is not None:
            version_id = _expect_str(raw.get("id"), f"{path}.id")
        name: Optional[str] = None
        if raw.get("name") is not None:
            name = _expect_str(raw.get("name"), f"{path}.name")
        project_id: Optional[int] = None
        if raw.get("projectId") is not None:
            project_id = _expect_int(raw.get("projectId"), f"{path}.projectId")
        released: Optional[bool] = None
        if raw.get("released") is not None:
            released = _expect_bool(raw.get("released"), f"{path}.released")
        release_date: Optional[str] = None
        if raw.get("releaseDate") is not None:
            # Versions can have date-only strings "2010-07-06"
            release_date = _expect_str(raw.get("releaseDate"), f"{path}.releaseDate")
        return Version(
            id=version_id,
            name=name,
            project_id=project_id,
            released=released,
            release_date=release_date,
        )

