from __future__ import annotations

import uuid
from collections.abc import Iterable
from dataclasses import dataclass
from datetime import UTC, datetime, timedelta
from pathlib import Path

import pytest
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

from scopewrit.crypto import TrustStore, sign_envelope
from scopewrit.engine import ScopeWritEngine
from scopewrit.models import Effect, ScopeManifest, ScopeRule, SignedEnvelope


@dataclass
class MutableClock:
    value: datetime

    def __call__(self) -> datetime:
        return self.value


@dataclass
class RuntimeFixture:
    engine: ScopeWritEngine
    manifest: ScopeManifest
    envelope: SignedEnvelope
    attestation: str
    principal_private: Ed25519PrivateKey
    issuer_private: Ed25519PrivateKey
    clock: MutableClock


def make_runtime(
    path: Path,
    *,
    rules: Iterable[ScopeRule] | None = None,
    pause_on_scope_uncertainty: bool = True,
    ticket_ttl_seconds: int = 30,
    clock: MutableClock | None = None,
) -> RuntimeFixture:
    clock = clock or MutableClock(datetime(2026, 7, 30, 18, 0, tzinfo=UTC))
    principal_private = Ed25519PrivateKey.generate()
    issuer_private = Ed25519PrivateKey.generate()
    rules = tuple(
        rules
        or (
            ScopeRule(
                rule_id="allow-example",
                effect=Effect.ALLOW,
                action="network.http",
                resource="https://example.com/authorized/**",
                methods=("GET",),
                max_uses=10,
            ),
        )
    )
    manifest = ScopeManifest(
        mandate_id=f"mandate-{uuid.uuid4().hex}",
        run_id=f"run-{uuid.uuid4().hex}",
        principal="test-principal",
        agent_id="test-agent",
        objective="Exercise a bounded action through the ScopeWrit test runtime.",
        issued_at=clock.value,
        not_before=clock.value,
        expires_at=clock.value + timedelta(minutes=10),
        nonce=uuid.uuid4().hex,
        rules=rules,
        pause_on_scope_uncertainty=pause_on_scope_uncertainty,
        ticket_ttl_seconds=ticket_ttl_seconds,
        claim_boundary=(
            "This test grants authority only through the explicitly guarded "
            "adapter and does not represent operating-system isolation."
        ),
    )
    envelope = sign_envelope(
        manifest.model_dump(mode="json"),
        principal_private,
        "principal-key",
    )
    engine = ScopeWritEngine(
        data_dir=path,
        principal_trust=TrustStore({"principal-key": principal_private.public_key()}),
        issuer_private_key=issuer_private,
        issuer_key_id="issuer-key",
        clock=clock,
    )
    started = engine.start_run(envelope)
    return RuntimeFixture(
        engine=engine,
        manifest=manifest,
        envelope=envelope,
        attestation=started.attestation,
        principal_private=principal_private,
        issuer_private=issuer_private,
        clock=clock,
    )


@pytest.fixture
def runtime(tmp_path: Path) -> RuntimeFixture:
    return make_runtime(tmp_path / "runtime")
