#!/usr/bin/env python3
"""Apply TawiPost's single hash-bound Postiz v2.21.8 auth hardening patch."""

from __future__ import annotations

import hashlib
import os
from pathlib import Path
import sys
import tempfile


EXPECTED_INPUT_SHA256 = "13405ccff967774e984d2f91f189a186022207ca1d6b8d504ac9e8f2cb0da5cc"
EXPECTED_OUTPUT_SHA256 = "f8421ebce7bb06483246d5692563c96167adbc57955f71f6521dd5acf9e9387a"
UPSTREAM_FALLBACK = "    return (await this._organizationService.getCount()) === 0;"
HARDENED_FALLBACK = "    return false;"


class PatchError(RuntimeError):
    """Raised when the pinned upstream source is not exactly what was reviewed."""


def sha256_text(source: str) -> str:
    return hashlib.sha256(source.encode("utf-8")).hexdigest()


def replace_registration_fallback(source: str) -> str:
    occurrences = source.count(UPSTREAM_FALLBACK)
    if occurrences != 1:
        raise PatchError(
            "expected exactly one Postiz LOCAL-registration fallback; "
            f"found {occurrences}"
        )
    return source.replace(UPSTREAM_FALLBACK, HARDENED_FALLBACK, 1)


def harden_auth_source(source: str) -> str:
    input_sha256 = sha256_text(source)
    if input_sha256 != EXPECTED_INPUT_SHA256:
        raise PatchError(
            "refusing unknown Postiz auth source: input SHA-256 "
            f"{input_sha256}, expected {EXPECTED_INPUT_SHA256}"
        )

    patched = replace_registration_fallback(source)
    output_sha256 = sha256_text(patched)
    if output_sha256 != EXPECTED_OUTPUT_SHA256:
        raise PatchError(
            "patched Postiz auth source SHA-256 mismatch: "
            f"{output_sha256}, expected {EXPECTED_OUTPUT_SHA256}"
        )
    return patched


def write_text_atomic(target: Path, content: str) -> None:
    """Replace target only after the complete validated output is durable."""
    temporary: Path | None = None
    try:
        with tempfile.NamedTemporaryFile(
            mode="w",
            encoding="utf-8",
            newline="",
            dir=target.parent,
            prefix=f".{target.name}.",
            delete=False,
        ) as handle:
            temporary = Path(handle.name)
            handle.write(content)
            handle.flush()
            os.fsync(handle.fileno())
        os.chmod(temporary, target.stat().st_mode)
        os.replace(temporary, target)
        temporary = None
    finally:
        if temporary is not None:
            temporary.unlink(missing_ok=True)


def main(argv: list[str]) -> int:
    if len(argv) != 2:
        print(f"usage: {argv[0]} <auth.service.ts>", file=sys.stderr)
        return 2

    target = Path(argv[1])
    try:
        source = target.read_text(encoding="utf-8")
        patched = harden_auth_source(source)
        write_text_atomic(target, patched)
    except (OSError, UnicodeError, PatchError) as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 1
    print(
        "hardened Postiz auth source "
        f"{EXPECTED_INPUT_SHA256} -> {EXPECTED_OUTPUT_SHA256}"
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv))
