from contextlib import contextmanager
import json
from pathlib import Path, PurePosixPath
import shutil
import time
import uuid

SHARED_DIRECTORY = Path("/opt/palantir/sidecars/shared-volumes/shared")
WAIT_TIMEOUT_SECONDS = 600


@contextmanager
def execute_cli(prepare_input, shared_directory=SHARED_DIRECTORY,
                timeout_seconds=WAIT_TIMEOUT_SECONDS):
    requests_directory = Path(shared_directory) / "requests"
    requests_directory.mkdir(parents=True, exist_ok=True)
    request = requests_directory / ("request-" + uuid.uuid4().hex)
    request.mkdir()
    started = False
    try:
        data_directory = request / "data"
        data_directory.mkdir()
        prepare_input(data_directory)
        (request / "start_flag").touch()
        started = True
        deadline = time.monotonic() + timeout_seconds
        done = request / "done_flag"
        while not done.is_file():
            if time.monotonic() >= deadline:
                raise TimeoutError(f"Sidecar result timed out: {request.name}")
            time.sleep(0.1)
        status = json.loads(done.read_text(encoding="utf-8"))
        if status["timed_out"]:
            raise TimeoutError(f"Sidecar command timed out: {request.name}")
        if status["return_code"] != 0 or status["errors"]:
            raise RuntimeError(f"Sidecar command failed: {status}")
        yield request
    finally:
        if started:
            try:
                (request / "close_flag").touch(exist_ok=True)
            except FileNotFoundError:
                # 종료 대기 시간이 지난 요청은 서버가 이미 정리했을 수 있습니다.
                pass
        else:
            shutil.rmtree(request)


def copy_dataset_files(source, data_directory):
    filesystem = source.filesystem()
    for status in filesystem.ls():
        relative = PurePosixPath(status.path)
        if relative.is_absolute() or ".." in relative.parts:
            raise ValueError(f"A dataset-relative file path is required: {status.path}")
        destination = Path(data_directory).joinpath(*relative.parts)
        destination.parent.mkdir(parents=True, exist_ok=True)
        with filesystem.open(status.path, "rb") as input_file:
            with destination.open("xb") as output_file:
                shutil.copyfileobj(input_file, output_file)


def copy_artifacts(output, request, prefix="", include_input=False):
    artifacts = [
        ("start_flag", request / "start_flag"),
        ("outfile.csv", request / "data" / "outfile.csv"),
        ("logfile", request / "logfile"),
        ("done_flag", request / "done_flag"),
    ]
    if include_input:
        artifacts.append(("infile.csv", request / "data" / "infile.csv"))
    filesystem = output.filesystem()
    for name, path in artifacts:
        with path.open("rb") as source:
            with filesystem.open(prefix + name, "wb") as destination:
                shutil.copyfileobj(source, destination)
