"""Dual-signature attestation endpoints.

GET  /v1/attestation/{id}    — Get signed attestation
POST /v1/attestation/verify  — Verify any attestation
"""

from __future__ import annotations

import hashlib
import json
from datetime import datetime, timezone
from uuid import uuid4

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from kredo_drift.server.auth import AuthenticatedAgent, require_api_key
from kredo_drift.server.db.models import DriftAttestationRecord, DriftTestRecord, AuditEvent
from kredo_drift.server.db.session import get_db
from kredo_drift.server.deps import get_service_signing_key, get_service_pubkey
from kredo_drift.canonical import canonical_json
from kredo_drift.signing import verify_attestation as verify_attestation_sig
from kredo_drift.models import DriftAttestation

router = APIRouter(tags=["attestation"])


class VerifyRequest(BaseModel):
    attestation: dict  # Full attestation JSON


@router.get("/attestation/{attestation_id}")
async def get_attestation(
    attestation_id: str,
    auth: AuthenticatedAgent = Depends(require_api_key),
    db: AsyncSession = Depends(get_db),
):
    """Get a signed attestation by ID."""
    result = await db.execute(
        select(DriftAttestationRecord).where(
            DriftAttestationRecord.id == attestation_id
        )
    )
    record = result.scalar_one_or_none()
    if not record:
        raise HTTPException(status_code=404, detail="Attestation not found")

    return json.loads(record.raw_json)


@router.post("/attestation/create")
async def create_attestation(
    auth: AuthenticatedAgent = Depends(require_api_key),
    db: AsyncSession = Depends(get_db),
):
    """Create a dual-signed attestation for the latest test result."""
    # Get latest test
    test_result = await db.execute(
        select(DriftTestRecord)
        .where(DriftTestRecord.agent_pubkey == auth.pubkey)
        .order_by(DriftTestRecord.created_at.desc())
        .limit(1)
    )
    test = test_result.scalar_one_or_none()
    if not test:
        raise HTTPException(status_code=404, detail="No test results found")

    service_pubkey = get_service_pubkey()

    # Build attestation
    attestation_data = {
        "kredo": "drift-attestation-v1",
        "type": "drift",
        "version": "1.0",
        "id": uuid4().hex,
        "attestor": service_pubkey,
        "subject": {
            "pubkey": auth.pubkey,
            "name": auth.agent_id,
        },
        "drift": {
            "score": test.score,
            "dimensions": {
                ds["dimension"]: {
                    "score": ds["score"],
                    "similarity": ds["similarity"],
                }
                for ds in test.dimension_scores
            },
            "classification": test.classification,
            "baseline_hash": test.baseline_hash,
            "test_hash": test.content_hash,
        },
        "prompt_selection_hash": hashlib.sha256(
            json.dumps(sorted(test.prompt_ids)).encode()
        ).hexdigest(),
        "chain_position": None,  # filled after chain append
        "chain_hash": None,
        "issued": datetime.now(timezone.utc).isoformat(),
        "expires": None,
    }

    # Service signs
    payload_bytes = canonical_json(attestation_data)
    service_sk = get_service_signing_key()
    service_sig = service_sk.sign(payload_bytes).signature.hex()
    attestation_data["service_signature"] = f"ed25519:{service_sig}"

    # Content hash
    content_hash = hashlib.sha256(payload_bytes).hexdigest()

    # Store
    record = DriftAttestationRecord(
        id=attestation_data["id"],
        content_hash=content_hash,
        agent_pubkey=auth.pubkey,
        version=attestation_data["kredo"],
        attestor=service_pubkey,
        test_hash=test.content_hash,
        score=test.score,
        dimension_scores=test.dimension_scores,
        classification=test.classification,
        prompt_selection_hash=attestation_data["prompt_selection_hash"],
        raw_json=json.dumps(attestation_data, default=str),
        service_signature=f"ed25519:{service_sig}",
    )
    db.add(record)

    db.add(AuditEvent(
        event_type="attestation_create",
        agent_pubkey=auth.pubkey,
        detail={"attestation_id": attestation_data["id"], "test_hash": test.content_hash},
    ))

    return attestation_data


@router.post("/attestation/{attestation_id}/countersign")
async def countersign_attestation(
    attestation_id: str,
    agent_signature: str,
    auth: AuthenticatedAgent = Depends(require_api_key),
    db: AsyncSession = Depends(get_db),
):
    """Agent countersigns an attestation, completing dual-signature."""
    from kredo_drift.server.auth import verify_request_signature

    result = await db.execute(
        select(DriftAttestationRecord).where(
            DriftAttestationRecord.id == attestation_id,
            DriftAttestationRecord.agent_pubkey == auth.pubkey,
        )
    )
    record = result.scalar_one_or_none()
    if not record:
        raise HTTPException(status_code=404, detail="Attestation not found")

    # Verify agent signature over content hash
    if not verify_request_signature(
        record.content_hash.encode(), agent_signature, auth.pubkey
    ):
        raise HTTPException(status_code=401, detail="Invalid countersignature")

    # Update the attestation with agent signature
    attestation_data = json.loads(record.raw_json)
    attestation_data["agent_signature"] = agent_signature
    record.agent_signature = agent_signature
    record.raw_json = json.dumps(attestation_data, default=str)

    db.add(AuditEvent(
        event_type="attestation_countersign",
        agent_pubkey=auth.pubkey,
        detail={"attestation_id": attestation_id},
    ))

    return {"status": "countersigned", "attestation_id": attestation_id}


@router.post("/attestation/verify")
async def verify_attestation_endpoint(
    req: VerifyRequest,
):
    """Verify any attestation's signatures. No auth required — public verifiability."""
    attestation = req.attestation

    results = {"valid": False, "checks": {}}

    # Check service signature
    service_sig = attestation.get("service_signature", "")
    if service_sig:
        service_pubkey = get_service_pubkey()
        # Strip signatures from payload for verification
        verify_data = {k: v for k, v in attestation.items()
                      if k not in ("service_signature", "agent_signature")}
        payload_bytes = canonical_json(verify_data)

        from kredo_drift.server.auth import verify_request_signature
        results["checks"]["service_signature"] = verify_request_signature(
            payload_bytes, service_sig, service_pubkey
        )

    # Check agent signature
    agent_sig = attestation.get("agent_signature", "")
    if agent_sig:
        agent_pubkey = attestation.get("subject", {}).get("pubkey", "")
        content_hash = hashlib.sha256(
            canonical_json({k: v for k, v in attestation.items()
                          if k not in ("service_signature", "agent_signature")})
        ).hexdigest()

        from kredo_drift.server.auth import verify_request_signature
        results["checks"]["agent_signature"] = verify_request_signature(
            content_hash.encode(), agent_sig, agent_pubkey
        )

    # WHY service signature alone = valid: The agent signature is a bonus
    # (dual-signed attestations are stronger), but the service signature alone
    # proves the Kredo server vouched for this result. This allows free-tier
    # agents (who may not have signing keys) to still produce verifiable
    # attestations backed by the service's authority.
    results["valid"] = results["checks"].get("service_signature", False)

    return results
