"""
Job storage: each uploaded file becomes a "job" with its own isolated
directory (named by a random UUID, never derived from user input) containing
the raw upload, the cleaned/rejected Parquet outputs, and a status file.

Kept intentionally simple (filesystem + JSON status file) for an MVP that's
easy to self-host. For heavier production use, swap the in-memory job lock
and background-thread execution for a real task queue (Celery/RQ/Arq) and
the status file for a row in Postgres/Redis — the interface here (`create_job`,
`get_status`, `set_status`) is small enough to swap out.
"""

import json
import shutil
import uuid
import threading
from datetime import datetime, timezone
from pathlib import Path

DATA_DIR = Path(__file__).parent / "data"
DATA_DIR.mkdir(exist_ok=True)

_locks: dict[str, threading.Lock] = {}
_locks_guard = threading.Lock()


def _job_lock(job_id: str) -> threading.Lock:
    with _locks_guard:
        if job_id not in _locks:
            _locks[job_id] = threading.Lock()
        return _locks[job_id]


def job_dir(job_id: str) -> Path:
    # job_id is always a server-generated uuid4 hex string — never build this
    # path from raw user input.
    d = DATA_DIR / job_id
    return d


def inputs_dir(job_id: str) -> Path:
    return job_dir(job_id) / "inputs"


def create_job(original_filenames: list[str], created_by: str | None = None) -> str:
    job_id = uuid.uuid4().hex
    d = job_dir(job_id)
    d.mkdir(parents=True, exist_ok=False)
    (d / "inputs").mkdir()
    set_status(
        job_id,
        status="pending",
        original_filenames=original_filenames,
        created_by=created_by,
        created_at=datetime.now(timezone.utc).isoformat(),
    )
    return job_id


def status_path(job_id: str) -> Path:
    return job_dir(job_id) / "status.json"


def set_status(job_id: str, **fields):
    with _job_lock(job_id):
        p = status_path(job_id)
        existing = {}
        if p.exists():
            existing = json.loads(p.read_text())
        existing.update(fields)
        existing["updated_at"] = datetime.now(timezone.utc).isoformat()
        p.write_text(json.dumps(existing, default=str))


def get_status(job_id: str) -> dict | None:
    p = status_path(job_id)
    if not p.exists():
        return None
    return json.loads(p.read_text())


def job_exists(job_id: str) -> bool:
    return job_dir(job_id).exists()


def delete_job(job_id: str):
    d = job_dir(job_id)
    if d.exists():
        shutil.rmtree(d)
    with _locks_guard:
        _locks.pop(job_id, None)


def list_jobs() -> list[str]:
    return [p.name for p in DATA_DIR.iterdir() if p.is_dir()]
