import logging
import os
from pathlib import Path
from typing import Optional
from zipfile import ZipFile

from ..authclient import AuthClient
from ..config import settings
from ..interface import Interface
from ..response import check_response


def get_job(
    client: AuthClient,
    *,
    project_id: int,
    job_id: int,
    batch: bool = False,
) -> Interface:
    """
    Query the status of a workflow-execution job
    """

    res = client.get(f"{settings.BASE_URL}/project/{project_id}/job/{job_id}/")
    job = check_response(res, expected_status_code=200)
    if batch:
        return Interface(retcode=0, data=job["status"])
    else:
        return Interface(retcode=0, data=job)


def get_job_list(
    client: AuthClient, *, project_id: int, batch: bool = False
) -> Interface:

    res = client.get(f"{settings.BASE_URL}/project/{project_id}/job/")
    jobs = check_response(res, expected_status_code=200)

    if batch:
        job_ids = " ".join(str(job["id"]) for job in jobs)
        return Interface(retcode=0, data=job_ids)
    else:
        return Interface(retcode=0, data=jobs)


def get_job_logs(
    client: AuthClient,
    *,
    project_id: int,
    job_id: int,
    output_folder: str,
) -> Interface:

    # Check that output_folder does not already exist
    if Path(output_folder).exists():
        return Interface(
            retcode=1, data=f"ERROR: {output_folder=} already exists"
        )

    # Send request to server
    res = client.get(
        f"{settings.BASE_URL}/project/{project_id}/job/{job_id}/download/"
    )

    # NOTE: We cannot use our default check_response here, because res._content
    # is binary. Therefore we check the status code by hand
    if res.status_code != 200:
        logging.error(f"Server returned {res.status_code}")
        logging.error(
            f"Original request: {res._request.method} {res._request.url}"
        )
        logging.error(
            f"Original payload: {res._request._content.decode('utf-8')}"
        )
        logging.error("Terminating.\n")
        exit(1)

    # Check the content-type entry in the response headers
    content_type = res.headers["content-type"]
    expected_content_type = "application/x-zip-compressed"
    if content_type != expected_content_type:
        logging.error(
            f"Unexpected {content_type=} in headers of server "
            f"response, instead of {expected_content_type=}"
        )
        logging.error(
            f"Original request: {res._request.method} {res._request.url}"
        )
        logging.error(
            f"Original payload: {res._request._content.decode('utf-8')}"
        )
        logging.error("Terminating.\n")
        exit(1)

    # Write response into a temporary zipped file
    zipped_archive_path = output_folder + "_tmp.zip"
    with open(zipped_archive_path, "wb") as f:
        f.write(res.content)

    # Unzip the log archive
    unzipped_archived_path = output_folder
    os.mkdir(unzipped_archived_path)
    with ZipFile(zipped_archive_path, mode="r") as zipfile:
        zipfile.extractall(path=unzipped_archived_path)

    # Remove zipped temporary file
    os.unlink(zipped_archive_path)

    return Interface(retcode=0, data=f"Logs downloaded to {output_folder=}")


def stop_job(client: AuthClient, *, project_id: int, job_id: int) -> Interface:
    """
    Stop a workflow-execution job
    """

    res = client.get(
        f"{settings.BASE_URL}/project/{project_id}/job/{job_id}/stop/"
    )
    check_response(res, expected_status_code=202)
    return Interface(
        retcode=0, data="Correctly called the job-stopping endpoint"
    )


def job_submit(
    client: AuthClient,
    *,
    project_id: int,
    workflow_id: int,
    dataset_id: int,
    first_task_index: Optional[int] = None,
    last_task_index: Optional[int] = None,
    worker_init: Optional[str] = None,
    batch: bool = False,
) -> Interface:

    job_submit = dict()
    # Prepare JobV2 object, without None attributes
    if worker_init is not None:
        job_submit["worker_init"] = worker_init
    if first_task_index is not None:
        job_submit["first_task_index"] = first_task_index
    if last_task_index is not None:
        job_submit["last_task_index"] = last_task_index

    # Prepare query parameters
    query_parameters = f"workflow_id={workflow_id}" f"&dataset_id={dataset_id}"

    res = client.post(
        (
            f"{settings.BASE_URL}/project/{project_id}/job/"
            f"submit/?{query_parameters}"
        ),
        json=job_submit,
    )
    job_read = check_response(res, expected_status_code=202)

    if batch:
        return Interface(retcode=0, data=job_read["id"])
    else:
        return Interface(retcode=0, data=job_read)
