"""Sanctuary service: immutable archival to IPFS + on-chain hash.

The sanctuary is the immutable layer of libreDecision.  Every adopted
document version, decision result, or vote tally is hashed (SHA-256),
stored on IPFS, and anchored on-chain via system.remark.
"""

from __future__ import annotations

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

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.sanctuary import SanctuaryEntry
from app.services import ipfs_service

logger = logging.getLogger(__name__)


async def archive_to_sanctuary(
    entry_type: str,
    reference_id: uuid.UUID,
    content: str,
    title: str,
    db: AsyncSession,
) -> SanctuaryEntry:
    """Hash content and create a sanctuary entry.

    Pipeline:
    1. Hash content (SHA-256)
    2. Try to upload to IPFS via ipfs_service (catch errors, log, continue)
    3. Try to anchor on-chain via blockchain_service (catch errors, log, continue)
    4. Create SanctuaryEntry with whatever succeeded

    Parameters
    ----------
    entry_type:
        Type of the archived entity (``"document"``, ``"decision"``,
        ``"vote_result"``).
    reference_id:
        UUID of the source entity (document, decision, or vote session).
    content:
        The full text content to archive and hash.
    title:
        Human-readable title for the archive entry.
    db:
        Async database session.

    Returns
    -------
    SanctuaryEntry
        The newly created sanctuary entry with content_hash set.
    """
    # 1. Compute SHA-256 hash of the content
    content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest()

    # Build metadata
    metadata: dict = {
        "archived_at": datetime.now(timezone.utc).isoformat(),
        "entry_type": entry_type,
        "content_length": len(content),
    }

    ipfs_cid: str | None = None
    chain_tx_hash: str | None = None
    chain_block: int | None = None

    # 2. Try to upload to IPFS
    try:
        ipfs_cid = await ipfs_service.upload_to_ipfs(content)
        if ipfs_cid:
            # Pin the content to keep it available
            await ipfs_service.pin(ipfs_cid)
            metadata["ipfs_cid"] = ipfs_cid
            logger.info("Contenu archive sur IPFS: CID=%s", ipfs_cid)
        else:
            logger.warning("Upload IPFS echoue (retour None) pour %s:%s", entry_type, reference_id)
    except Exception:
        logger.warning(
            "Erreur lors de l'upload IPFS pour %s:%s",
            entry_type, reference_id,
            exc_info=True,
        )

    # 3. Try to anchor on-chain (still a structured stub)
    try:
        chain_tx_hash, chain_block = await _anchor_on_chain(content_hash)
        if chain_tx_hash:
            metadata["chain_tx_hash"] = chain_tx_hash
            metadata["chain_block"] = chain_block
            logger.info("Hash ancre on-chain: tx=%s block=%s", chain_tx_hash, chain_block)
    except NotImplementedError:
        logger.info("Ancrage on-chain pas encore implemente, etape ignoree")
    except Exception:
        logger.warning(
            "Erreur lors de l'ancrage on-chain pour %s:%s",
            entry_type, reference_id,
            exc_info=True,
        )

    # 4. Create SanctuaryEntry with whatever succeeded
    entry = SanctuaryEntry(
        entry_type=entry_type,
        reference_id=reference_id,
        title=title,
        content_hash=content_hash,
        ipfs_cid=ipfs_cid,
        chain_tx_hash=chain_tx_hash,
        chain_block=chain_block,
        metadata_json=json.dumps(metadata, ensure_ascii=False),
    )

    db.add(entry)
    await db.commit()
    await db.refresh(entry)

    return entry


async def verify_entry(
    entry_id: uuid.UUID,
    db: AsyncSession,
) -> dict:
    """Verify the integrity of a sanctuary entry.

    Re-fetches the content (from IPFS if available) and re-hashes it
    to compare with the stored content_hash.

    Parameters
    ----------
    entry_id:
        UUID of the SanctuaryEntry to verify.
    db:
        Async database session.

    Returns
    -------
    dict
        Verification result with keys:
        - ``entry_id``: UUID of the entry
        - ``valid``: bool indicating if the hash matches
        - ``stored_hash``: the stored content_hash
        - ``computed_hash``: the re-computed hash (or None if content unavailable)
        - ``source``: where the content was fetched from (``"ipfs"`` or ``"unavailable"``)
        - ``detail``: human-readable detail message

    Raises
    ------
    ValueError
        If the entry is not found.
    """
    result = await db.execute(
        select(SanctuaryEntry).where(SanctuaryEntry.id == entry_id)
    )
    entry = result.scalar_one_or_none()
    if entry is None:
        raise ValueError(f"Entree sanctuaire introuvable : {entry_id}")

    stored_hash = entry.content_hash
    computed_hash: str | None = None
    source = "unavailable"

    # Try to re-fetch content from IPFS
    if entry.ipfs_cid:
        try:
            content_bytes = await ipfs_service.get_from_ipfs(entry.ipfs_cid)
            if content_bytes is not None:
                computed_hash = hashlib.sha256(content_bytes).hexdigest()
                source = "ipfs"
        except Exception:
            logger.warning(
                "Impossible de recuperer le contenu IPFS pour verification (CID=%s)",
                entry.ipfs_cid,
                exc_info=True,
            )

    if computed_hash is None:
        return {
            "entry_id": entry.id,
            "valid": False,
            "stored_hash": stored_hash,
            "computed_hash": None,
            "source": source,
            "detail": "Contenu indisponible pour la verification",
        }

    is_valid = computed_hash == stored_hash
    return {
        "entry_id": entry.id,
        "valid": is_valid,
        "stored_hash": stored_hash,
        "computed_hash": computed_hash,
        "source": source,
        "detail": "Integrite verifiee" if is_valid else "Hash different - contenu potentiellement altere",
    }


async def get_entries_by_reference(
    reference_id: uuid.UUID,
    db: AsyncSession,
) -> list[SanctuaryEntry]:
    """Query all sanctuary entries for a given reference_id.

    Parameters
    ----------
    reference_id:
        UUID of the referenced entity (document, decision, etc.).
    db:
        Async database session.

    Returns
    -------
    list[SanctuaryEntry]
        All entries matching the reference_id, ordered by creation date desc.
    """
    result = await db.execute(
        select(SanctuaryEntry)
        .where(SanctuaryEntry.reference_id == reference_id)
        .order_by(SanctuaryEntry.created_at.desc())
    )
    return list(result.scalars().all())


async def _anchor_on_chain(content_hash: str) -> tuple[str | None, int | None]:
    """Anchor a content hash on-chain via system.remark.

    Currently a stub. When implemented, this will use substrate-interface
    to submit a system.remark extrinsic containing the content hash.

    Example::

        from substrateinterface import SubstrateInterface
        from app.config import settings

        substrate = SubstrateInterface(url=settings.DUNITER_RPC_URL)
        call = substrate.compose_call(
            call_module="System",
            call_function="remark",
            call_params={"remark": f"libredecision:sanctuary:{content_hash}"},
        )
        extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
        receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)
        return receipt.extrinsic_hash, receipt.block_number

    Parameters
    ----------
    content_hash:
        The SHA-256 hash to anchor.

    Returns
    -------
    tuple[str | None, int | None]
        (tx_hash, block_number) or (None, None) if not implemented.
    """
    raise NotImplementedError("Ancrage on-chain pas encore implemente")
