from pathlib import Path

# from typing import Callable
# from tierkreis.controller.executor.hpc.hpc_executor import run_hpc_executor
from tierkreis.controller.executor.hpc.job_spec import JobSpec


_COMMAND_PREFIX = "#PBS"


def generate_pbs_script(spec: JobSpec) -> str:
    # 1. Shebang and file header
    lines = [
        """#!/bin/bash
#
# PBS Job Script generated by TIERKREIS

# --- Core Job Specifications ---"""
    ]
    # 2. Name
    lines.append(f"{_COMMAND_PREFIX} -N {spec.job_name}")
    # 3. Resources (node exclusive)
    lines.append(f"{_COMMAND_PREFIX} -l walltime={spec.walltime}")
    resources = f"{_COMMAND_PREFIX} -l select={spec.resource.nodes}"
    if spec.resource.cores_per_node is not None:
        resources += f":ncpus={spec.resource.cores_per_node}"
    if spec.resource.memory_gb is not None:
        resources += f":mem={spec.resource.memory_gb}gb"
    if spec.resource.gpus_per_node is not None:
        resources += f":ngpus={spec.resource.gpus_per_node}"
    if spec.mpi is not None and spec.mpi.max_proc_per_node is not None:
        resources += f":mpiprocs={spec.mpi.max_proc_per_node}"
    if spec.container is not None:
        resources += f":container_engine={spec.container.engine}"
    lines.append(resources)
    if spec.queue is not None:
        lines.append(f"{_COMMAND_PREFIX} -q {spec.queue}")

    # 4. User settings
    lines.append("\n# --- User Details ---")
    if spec.account is not None:
        lines.append(f"{_COMMAND_PREFIX} -A {spec.account}")
    if spec.user is not None:
        if spec.user.mail is not None:
            lines.append(f"{_COMMAND_PREFIX} -m e")  # end only
            lines.append(f"{_COMMAND_PREFIX} -M {spec.user.mail}")

    # 5. Output and Error handling
    lines.append("\n# --- Output and Error Handling ---")
    if spec.error_path is not None:
        lines.append(f"{_COMMAND_PREFIX} -e {spec.error_path}")
    else:
        lines.append(f"{_COMMAND_PREFIX} -j oe")
    if spec.output_path is None:
        spec.output_path = Path(f"./{spec.job_name}.o%j")
    lines.append(f"{_COMMAND_PREFIX} -o {spec.output_path}")

    # 6. MPI
    if spec.mpi is not None:
        lines.append("\n# --- MPI ---")
        lines.append("\n MPI is covered in -l resource declaration")

    # 7. User specific
    lines.append("\n# --- User-Specific Arguments ---")
    for key, value in spec.extra_scheduler_args.items():
        lines.append(f"{_COMMAND_PREFIX} {key} {value if value is not None else ''}")

    # 8. Environment
    lines.append("\n# --- Environment Setup ---")
    if spec.environment != {}:
        env = ",".join(
            f"{key}={value if value else '""'}"
            for key, value in spec.environment.items()
        )
        lines.append(f"-v w{env}")
    # 9. Container logic # taken from nscc docs for enroot:
    if spec.container is not None:
        lines.append(f"{_COMMAND_PREFIX} -l container_image={spec.container.image}")
        if spec.container.name is not None:
            lines.append(f"{_COMMAND_PREFIX} -l container_name={spec.container.name}")
        for key, value in spec.container.extra_args.items():
            lines.append(f"{_COMMAND_PREFIX} -l {key}={value}")
        if spec.container.env_file is not None:
            lines.append(
                f"{_COMMAND_PREFIX} -l {spec.container.engine}_env_file={spec.container.env_file}"
            )  # check if this makes sense for others beside enroot

    # 10. User Command, (prologue), command, (epilogue)
    lines.append("\n# --- User Command ---")
    lines.append(spec.command)

    return "\n".join(lines)


# Disabled for now, needs testing with a PBS system, will be re-enabled later
# See: Issue #182
# class PBSExecutor:
#     def __init__(
#         self,
#         registry_path: Path | None,
#         logs_path: Path,
#         spec: JobSpec,
#         command: str = "qsub",
#     ) -> None:
#         self.launchers_path = registry_path
#         self.logs_path = logs_path
#         self.errors_path = logs_path
#         self.spec = spec
#         self.script_fn: Callable[[JobSpec], str] = generate_pbs_script
#         self.command = command

#     def run(self, launcher_name: str, worker_call_args_path: Path) -> None:
#         self.errors_path = (
#             self.logs_path.parent.parent / worker_call_args_path.parent / "errors"
#         )
#         run_hpc_executor(self, launcher_name, worker_call_args_path)
