import json
from dataclasses import asdict, dataclass, field
from io import BytesIO
from typing import Any, List, Optional
from uuid import UUID

# @TODO verify if we still need this one and remove


@dataclass
class GenerateFiles:
    project_info_content: BytesIO
    archive_content: BytesIO


@dataclass
class PAProjectInfo:
    title: str
    project_id: str
    notebook_file_name: str
    notebook_file_size: str
    environment: str
    path: str
    description: str = ""
    last_published_at: str = ""
    versions: List[str] = field(default_factory=list)

    def to_bytes_io(self):
        return BytesIO(json.dumps(asdict(self)).encode("utf-8"))


@dataclass
class AppVersion:
    id: UUID
    version: str
    created_at: str
    updated_at: str
    last_published_at: Optional[str] = None

    @classmethod
    def create_from_microservice_response(self, json_response: dict) -> "ProjectInfoData":
        return AppVersion(
            id=json_response["id"],
            version=json_response["version"],
            created_at=json_response["created_at"],
            updated_at=json_response["updated_at"],
            last_published_at=json_response["last_published_at"],
        )


@dataclass
class VersioningData:
    next_version: str
    current_version: str = ""
    all_versions: List[AppVersion] = field(default_factory=list)


@dataclass
class Domain:
    webapp_id: int
    domain: str
    user: str
    enabled: bool


@dataclass
class ProjectInfoData:
    title: str
    project_id: str
    versioning: VersioningData
    notebook_file_name: str
    notebook_file_size: str
    permission: str
    environment: str
    status: str = ""
    description: str = ""
    last_published_at: str = ""
    domains: List[Domain] = field(default_factory=list)

    def is_active(self):
        # TODO : THIS SHOULD BECOME A ENUM
        return self.status == "published"

    @classmethod
    def create_from_microservice_response(self, json_response: dict) -> "ProjectInfoData":
        return ProjectInfoData(
            title=json_response["title"],
            project_id=json_response["id"],
            versioning=VersioningData(
                next_version=json_response["versioning"]["next_version"],
                current_version=json_response["versioning"]["current_version"],
                all_versions=json_response["versioning"]["all_versions"],
            ),
            notebook_file_name=json_response["notebook_file_name"],
            notebook_file_size=json_response["notebook_file_size"],
            permission=json_response["permission"],
            environment=json_response["environment"],
            description=json_response["description"],
            last_published_at=json_response["last_published_at"],
            domains=json_response["domains"],
            status=json_response["status"],
        )


@dataclass
class FileInfo:
    file_name: str
    file_size: str


@dataclass
class RequestError:
    status: bool
    message: str = ""


@dataclass
class Deployment:
    id: int = None
    user: str = None
    domains: List[Domain] = field(default_factory=list)
    command: Optional[str] = None
    domain_name: Optional[str] = None
    webapp: Optional[int] = None
    enabled: Optional[bool] = None


@dataclass
class ListDeploymentsResponse:
    deployments: List[Deployment] = field(default_factory=list)


@dataclass
class MetaResponse:
    error: RequestError


@dataclass
class ServiceResponse:
    meta: MetaResponse
    content: Any

    def has_succeeded(self) -> bool:
        return not self.meta.error.status

    def has_failed(self) -> bool:
        return self.meta.error.status

    def set_error_message(self, msg: str) -> bool:
        if not self.meta and not self.meta.error:
            return False

        self.meta.error.message = msg
        return True

    def get_error_message(self) -> str:
        if not self.meta and not self.meta.error:
            return ""

        return self.meta.error.message

    def set_error_status(self, status: bool) -> bool:
        if not self.meta and not self.meta.error:
            return False
        self.meta.error.status = status
        return True


#########################################################################
#                       REQUEST SCHEMAS                                 #
#########################################################################
@dataclass
class Webapp:
    command: str


@dataclass
class CreatePanelAppRequest:
    domain_name: str
    enabled: bool
    webapp: Webapp


@dataclass
class PublishAppRequest:  # let's redeclare it just in case PA change the contract
    enabled: bool = True


@dataclass
class UnpublishAppRequest:  # let's redeclare it just in case PA change the contract
    enabled: bool = False


#########################################################################
#                       RESPONSE SCHEMAS                                #
#########################################################################
@dataclass
class WebappResponse:
    id: int
    domain_name: str
    enabled: bool
    status: Optional[str] = None


@dataclass
class CreatePanelAppResponse:
    app: WebappResponse


@dataclass
class ListAppsResponse:
    num_active_apps: int
    num_max_apps: int
    apps: List[ProjectInfoData]
