"""
BaseWorker methods for tasks.
"""

import uuid
from collections.abc import Iterator
from http.client import REQUEST_TIMEOUT
from pathlib import Path

import magic
import requests

from arkindex.compat import DownloadedFile
from arkindex_worker import logger
from arkindex_worker.models import Artifact
from teklia_toolbox.requests import should_verify_cert


class TaskMixin:
    def list_artifacts(self, task_id: uuid.UUID) -> Iterator[Artifact]:
        """
        List artifacts associated to a task.

        :param task_id: Task ID to find artifacts from.
        :returns: An iterator of ``Artifact`` objects built from the ``ListArtifacts`` API endpoint.
        """
        assert task_id and isinstance(task_id, uuid.UUID), (
            "task_id shouldn't be null and should be an UUID"
        )

        results = self.api_client.request("ListArtifacts", id=task_id)

        return map(Artifact, results)

    def download_artifact(
        self, task_id: uuid.UUID, artifact: Artifact
    ) -> DownloadedFile:
        """
        Download an artifact content.

        :param task_id: Task ID the Artifact is from.
        :param artifact: Artifact to download content from.
        :returns: A temporary file containing the ``Artifact`` downloaded from the ``DownloadArtifact`` API endpoint.
        """
        assert task_id and isinstance(task_id, uuid.UUID), (
            "task_id shouldn't be null and should be an UUID"
        )
        assert artifact and isinstance(artifact, Artifact), (
            "artifact shouldn't be null and should be an Artifact"
        )

        return self.api_client.request(
            "DownloadArtifact", id=task_id, path=artifact.path
        )

    def upload_artifact(self, path: Path) -> None:
        """
        Upload a single file as an Artifact of the current task.

        :param path: Path of the single file to upload as an Artifact.
        """
        assert path and isinstance(path, Path) and path.exists(), (
            "path shouldn't be null, should be a Path and should exist"
        )

        if self.is_read_only:
            logger.warning("Cannot upload artifact as this worker is in read-only mode")
            return

        # Get path relative to task's data directory
        relpath = str(path.relative_to(self.work_dir))

        # Get file size
        size = path.stat().st_size

        # Detect content type
        try:
            content_type = magic.from_file(path, mime=True)
        except Exception as e:
            logger.warning(f"Failed to get a mime type for {path}: {e}")
            content_type = "application/octet-stream"

        # Create artifact on API to get an S3 url
        artifact = self.api_client.request(
            "CreateArtifact",
            id=self.task_id,
            body={"path": relpath, "content_type": content_type, "size": size},
        )

        # Upload the file content to S3
        s3_put_url = artifact["s3_put_url"]
        with path.open("rb") as content:
            resp = requests.put(
                s3_put_url,
                data=content,
                headers={"Content-Type": content_type},
                timeout=REQUEST_TIMEOUT,
                verify=should_verify_cert(s3_put_url),
            )
            resp.raise_for_status()
