from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field


class CmdFile(BaseModel):
    src: Optional[str] = None
    content: Optional[str] = None
    file_id: Optional[str] = Field(None, serialization_alias="fileId")
    name: Optional[str] = None
    max: Optional[int] = 400 * 1024 * 1024
    symlink: Optional[str] = None
    stream_in: Optional[bool] = Field(None, serialization_alias="streamIn")
    stream_out: Optional[bool] = Field(None, serialization_alias="streamOut")
    pipe: Optional[bool] = None


class Cmd(BaseModel):
    args: Optional[List[str]] = None
    env: Optional[List[str]] = ["PATH=/usr/bin:/bin:/usr/local/bin"]
    stdin: Optional[CmdFile] = CmdFile(content="")
    stdout: Optional[CmdFile] = CmdFile(name="stdout", max=4 * 1024)
    stderr: Optional[CmdFile] = CmdFile(name="stderr", max=4 * 1024)
    cpu_limit: int = Field(4 * 1000000000000, serialization_alias="cpuLimit")
    real_cpu_limit: int = Field(0, serialization_alias="realCpuLimit")
    clock_limit: int = Field(8 * 1000000000000, serialization_alias="clockLimit")
    memory_limit: int = Field(800 * 1024 * 1024, serialization_alias="memoryLimit")
    stack_limit: int = Field(0, serialization_alias="stackLimit")
    proc_limit: int = Field(50, serialization_alias="procLimit")
    cpu_rate_limit: int = Field(0, serialization_alias="cpuRateLimit")
    cpu_set_limit: str = Field("", serialization_alias="cpuSetLimit")
    copy_in: Dict[str, CmdFile] = Field({}, serialization_alias="copyIn")
    copy_in_cached: Dict[str, str] = Field({}, serialization_alias="copyInCached")
    copy_in_dir: str = Field(".", serialization_alias="copyInDir")
    # reconsider this default situation
    copy_out: List[str] = Field(["stdout", "stderr"], serialization_alias="copyOut")
    copy_out_cached: List[str] = Field([], serialization_alias="copyOutCached")
    copy_out_max: int = Field(0, serialization_alias="copyOutMax")
    copy_out_dir: str = Field("", serialization_alias="copyOutDir")
    tty: bool = False
    strict_memory_limit: bool = Field(False, serialization_alias="strictMemoryLimit")
    data_segment_limit: bool = Field(False, serialization_alias="dataSegmentLimit")
    address_space_limit: bool = Field(False, serialization_alias="addressSpaceLimit")


class OptionalCmd(BaseModel):
    args: Optional[list[str]] = None
    env: Optional[list[str]] = ["PATH=/usr/bin:/bin:/usr/local/bin"]
    stdin: Optional[CmdFile] = None
    stdout: Optional[CmdFile] = None
    stderr: Optional[CmdFile] = None
    cpu_limit: Optional[int] = Field(4 * 1000000000000, serialization_alias="cpuLimit")
    real_cpu_limit: Optional[int] = Field(None, serialization_alias="realCpuLimit")
    clock_limit: Optional[int] = Field(
        8 * 1000000000000, serialization_alias="clockLimit"
    )
    memory_limit: Optional[int] = Field(
        800 * 1024 * 1024, serialization_alias="memoryLimit"
    )
    stack_limit: Optional[int] = Field(None, serialization_alias="stackLimit")
    proc_limit: Optional[int] = Field(50, serialization_alias="procLimit")
    cpu_rate_limit: Optional[int] = Field(None, serialization_alias="cpuRateLimit")
    cpu_set_limit: Optional[str] = Field(None, serialization_alias="cpuSetLimit")
    copy_in: Optional[Dict[str, CmdFile]] = Field(None, serialization_alias="copyIn")
    copy_in_cached: Optional[Dict[str, str]] = Field(
        None, serialization_alias="copyInCached"
    )
    copy_in_dir: Optional[str] = Field(None, serialization_alias="copyInDir")
    copy_out: Optional[List[str]] = Field(
        ["stdout", "stderr"], serialization_alias="copyOut"
    )
    copy_out_cached: Optional[List[str]] = Field(
        None, serialization_alias="copyOutCached"
    )
    copy_out_max: Optional[int] = Field(None, serialization_alias="copyOutMax")
    copy_out_dir: Optional[str] = Field(None, serialization_alias="copyOutDir")
    tty: Optional[bool] = None
    strict_memory_limit: Optional[bool] = Field(
        None, serialization_alias="strictMemoryLimit"
    )
    data_segment_limit: Optional[bool] = Field(
        None, serialization_alias="dataSegmentLimit"
    )
    address_space_limit: Optional[bool] = Field(
        None, serialization_alias="addressSpaceLimit"
    )


class ExecutorWith(BaseModel):
    default: Cmd
    cases: List[OptionalCmd]


class Executor(BaseModel):
    name: str
    with_: ExecutorWith = Field(..., serialization_alias="with")


class ParserConfig(BaseModel):
    name: str
    with_: Dict[str, Any] = Field(..., serialization_alias="with")


class StageDetail(BaseModel):
    name: str
    group: Optional[str] = ""
    executor: Executor
    parsers: List[ParserConfig]


class Stage(BaseModel):
    sandbox_exec_server: str = Field(
        "172.17.0.1:5051", serialization_alias="sandboxExecServer"
    )
    sandbox_token: str = Field("", serialization_alias="sandboxToken")
    output_path: str = Field(
        "/tmp/joj3_result.json", serialization_alias="outputPath"
    )  # nosec: B108
    stages: List[StageDetail]
    prestages: Optional[List[StageDetail]] = None
    poststages: List[StageDetail]


class Config(BaseModel):
    name: str = ""
    log_path: str = Field("", serialization_alias="logPath")
    expire_unix_timestamp: int = Field(-1, serialization_alias="expireUnixTimestamp")
    effective_unix_timestamp: int = Field(
        -1, serialization_alias="effectiveUnixTimestamp"
    )
    actor_csv_path: str = Field("", serialization_alias="actorCsvPath")
    max_total_score: int = Field(100, serialization_alias="maxTotalScore")
    stage: Stage