# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Genesis Block Bridge Layer

Connects the training skill's off-chain provenance system to the
on-chain genesis block provenance chain. Each chain-of-custody entry
and dataset export is anchored as a signed block.

Bridge functions are no-ops if no genesis block exists yet — this
allows the training skill to operate normally before genesis creation.
"""

import hashlib
import logging
from datetime import datetime, timezone

logger = logging.getLogger(__name__)


def _get_chain_dir():
    """Get chain directory (tenant-scoped in Reflection, default in standalone)."""
    try:
        from familiar.core.paths import CHAIN_DIR

        return CHAIN_DIR
    except ImportError:
        from pathlib import Path

        return Path.home() / ".familiar" / "data" / "chain"


def _get_store():
    """Get or create the ChainStore singleton for this session."""
    from familiar.core.genesis import ChainStore

    chain_dir = _get_chain_dir()
    chain_dir.mkdir(parents=True, exist_ok=True)
    return ChainStore(chain_dir / "chain.db")


def _get_keypair():
    """Load or create the RSA keypair."""
    from familiar.core.genesis import load_or_create_keypair

    key_dir = _get_chain_dir() / "keys"
    return load_or_create_keypair(key_dir)


def create_familiar_genesis(
    owner_name: str,
    owner_orcid: str,
    owner_email: str = "",
    expertise_domains: dict = None,
    template_source: str = "familiar_v1.4.0",
) -> str:
    """
    Create a genesis block for this Familiar instance.

    Returns the genesis block hash.
    Raises RuntimeError if a genesis block already exists (immutable — patent constraint).
    """
    from familiar.core.genesis import (
        ChainBlock,
        GenesisBlock,
        ProfessionalCredentialAnchor,
        ProfessionalOwnershipFramework,
        ProfessionalTemplateDerivation,
        ProvenanceChainInit,
        VerifiedCapabilityAttestation,
    )

    store = _get_store()

    if store.get_genesis() is not None:
        raise RuntimeError(
            "Genesis block already exists. The genesis block is immutable "
            "per patent constraint — it cannot be replaced or recreated."
        )

    private_key, public_key = _get_keypair()
    now = datetime.now(timezone.utc).isoformat()

    # Build credential hash from owner identity
    cred_bundle = f"{owner_name}:{owner_orcid}:{owner_email}"
    credential_hash = hashlib.sha256(cred_bundle.encode()).hexdigest()

    # (a) PCA
    pca = ProfessionalCredentialAnchor(
        credential_hash=credential_hash,
        authority_signatures=[],
        competency_metadata={"owner_name": owner_name, "email": owner_email},
        regulatory_attestations=[],
        orcid=owner_orcid,
    )

    # (b) PTD
    ptd = ProfessionalTemplateDerivation(
        template_source_id=template_source,
        customization_params={"platform": "familiar", "version": "1.4.0"},
        individualization_sigs=[],
        evolution_provenance=[template_source],
    )

    # (c) VCA
    domains = expertise_domains or {"ai_curation": ["training_data", "provenance"]}
    vca = VerifiedCapabilityAttestation(
        expertise_domains=domains,
        quality_thresholds={"min_curation_quality": 0.5},
        standard_refs=[],
        validation_proofs=[],
    )

    # (d) POF — single-owner control
    from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat

    pub_pem = public_key.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
    pof = ProfessionalOwnershipFramework(
        multi_sig_control={"owner": pub_pem.decode()},
        operator_authorization={"owner_name": owner_name, "orcid": owner_orcid},
        succession_params={},
        delegation_matrix={"owner": ["full_control"]},
    )

    # (e) PCI
    pci = ProvenanceChainInit(
        creation_timestamp=now,
        witness_proofs=[],
        authorization_sigs=[],
        audit_anchors=[],
    )

    # Build, hash, and sign the genesis block
    genesis = GenesisBlock(pca=pca, ptd=ptd, vca=vca, pof=pof, pci=pci)
    genesis.compute_hash()
    genesis.sign(private_key)

    # Wrap as ChainBlock index 0
    chain_block = ChainBlock(
        index=0,
        timestamp=now,
        prev_hash="0" * 64,
        block_hash="",
        block_type="genesis",
        payload=genesis.to_dict(),
    )
    chain_block.compute_hash()
    chain_block.sign(private_key)

    store.append(chain_block)
    logger.info("Genesis block created: %s", chain_block.block_hash[:16])
    return chain_block.block_hash


def anchor_provenance(custody_entry: dict) -> str:
    """
    Anchor a chain-of-custody entry from _record_provenance() to the chain.

    Returns the new block hash, or empty string if no genesis exists.
    """
    from familiar.core.genesis import ChainBlock

    store = _get_store()
    genesis = store.get_genesis()
    if genesis is None:
        return ""

    private_key, _ = _get_keypair()
    latest = store.get_latest()
    now = datetime.now(timezone.utc).isoformat()

    block = ChainBlock(
        index=latest.index + 1,
        timestamp=now,
        prev_hash=latest.block_hash,
        block_hash="",
        block_type="provenance",
        payload=custody_entry,
    )
    block.compute_hash()
    block.sign(private_key)
    store.append(block)

    logger.debug("Anchored provenance block %d: %s", block.index, block.block_hash[:16])
    return block.block_hash


def anchor_export(export_meta: dict) -> str:
    """
    Anchor a dataset export (with its SHA-256 fingerprint) to the chain.

    Returns the new block hash, or empty string if no genesis exists.
    """
    from familiar.core.genesis import ChainBlock

    store = _get_store()
    genesis = store.get_genesis()
    if genesis is None:
        return ""

    private_key, _ = _get_keypair()
    latest = store.get_latest()
    now = datetime.now(timezone.utc).isoformat()

    block = ChainBlock(
        index=latest.index + 1,
        timestamp=now,
        prev_hash=latest.block_hash,
        block_hash="",
        block_type="export",
        payload=export_meta,
    )
    block.compute_hash()
    block.sign(private_key)
    store.append(block)

    logger.debug("Anchored export block %d: %s", block.index, block.block_hash[:16])
    return block.block_hash


def anchor_consent(consent_entry: dict) -> str:
    """
    Anchor a consent grant/revocation to the chain.

    Returns the new block hash, or empty string if no genesis exists.
    """
    from familiar.core.genesis import ChainBlock

    store = _get_store()
    genesis = store.get_genesis()
    if genesis is None:
        return ""

    private_key, _ = _get_keypair()
    latest = store.get_latest()
    now = datetime.now(timezone.utc).isoformat()

    block = ChainBlock(
        index=latest.index + 1,
        timestamp=now,
        prev_hash=latest.block_hash,
        block_hash="",
        block_type="consent",
        payload=consent_entry,
    )
    block.compute_hash()
    block.sign(private_key)
    store.append(block)

    logger.debug("Anchored consent block %d: %s", block.index, block.block_hash[:16])
    return block.block_hash


def anchor_erasure(erasure_entry: dict) -> str:
    """
    Anchor a data erasure event to the chain.

    Returns the new block hash, or empty string if no genesis exists.
    """
    from familiar.core.genesis import ChainBlock

    store = _get_store()
    genesis = store.get_genesis()
    if genesis is None:
        return ""

    private_key, _ = _get_keypair()
    latest = store.get_latest()
    now = datetime.now(timezone.utc).isoformat()

    block = ChainBlock(
        index=latest.index + 1,
        timestamp=now,
        prev_hash=latest.block_hash,
        block_hash="",
        block_type="erasure",
        payload=erasure_entry,
    )
    block.compute_hash()
    block.sign(private_key)
    store.append(block)

    logger.debug("Anchored erasure block %d: %s", block.index, block.block_hash[:16])
    return block.block_hash


def anchor_training_run(training_meta: dict) -> str:
    """
    Anchor a model training run to the chain.

    Payload includes dataset fingerprint, hyperparams, model hash, eval results.
    Returns the new block hash, or empty string if no genesis exists.
    """
    from familiar.core.genesis import ChainBlock

    store = _get_store()
    genesis = store.get_genesis()
    if genesis is None:
        return ""

    private_key, _ = _get_keypair()
    latest = store.get_latest()
    now = datetime.now(timezone.utc).isoformat()

    block = ChainBlock(
        index=latest.index + 1,
        timestamp=now,
        prev_hash=latest.block_hash,
        block_hash="",
        block_type="training",
        payload=training_meta,
    )
    block.compute_hash()
    block.sign(private_key)
    store.append(block)

    logger.debug("Anchored training block %d: %s", block.index, block.block_hash[:16])
    return block.block_hash


def anchor_usage(usage_entry: dict) -> str:
    """
    Anchor a cross-peer usage event to the chain.

    Returns the new block hash, or empty string if no genesis exists.
    """
    from familiar.core.genesis import ChainBlock

    store = _get_store()
    genesis = store.get_genesis()
    if genesis is None:
        return ""

    private_key, _ = _get_keypair()
    latest = store.get_latest()
    now = datetime.now(timezone.utc).isoformat()

    block = ChainBlock(
        index=latest.index + 1,
        timestamp=now,
        prev_hash=latest.block_hash,
        block_hash="",
        block_type="usage",
        payload=usage_entry,
    )
    block.compute_hash()
    block.sign(private_key)
    store.append(block)

    logger.debug("Anchored usage block %d: %s", block.index, block.block_hash[:16])
    return block.block_hash


def anchor_watermark(watermark_meta: dict) -> str:
    """
    Anchor an artwork watermark registration to the chain.

    Payload should include artwork_id, title, artist, original_hash,
    watermarked_hash, watermark_types, license, certificate_hash, and metadata.

    Returns the new block hash, or empty string if no genesis exists.
    """
    from familiar.core.genesis import ChainBlock

    store = _get_store()
    genesis = store.get_genesis()
    if genesis is None:
        return ""

    private_key, _ = _get_keypair()
    latest = store.get_latest()
    now = datetime.now(timezone.utc).isoformat()

    block = ChainBlock(
        index=latest.index + 1,
        timestamp=now,
        prev_hash=latest.block_hash,
        block_hash="",
        block_type="watermark",
        payload=watermark_meta,
    )
    block.compute_hash()
    block.sign(private_key)
    store.append(block)

    logger.debug("Anchored watermark block %d: %s", block.index, block.block_hash[:16])
    return block.block_hash


def get_chain_status() -> dict:
    """
    Return chain summary: genesis hash, length, latest block, integrity check.
    """
    store = _get_store()
    genesis = store.get_genesis()

    if genesis is None:
        return {
            "initialized": False,
            "genesis_hash": None,
            "chain_length": 0,
            "latest_block": None,
            "integrity": None,
        }

    latest = store.get_latest()
    valid, error = store.verify_chain()

    return {
        "initialized": True,
        "genesis_hash": genesis.block_hash,
        "chain_length": store.chain_length(),
        "latest_block": {
            "index": latest.index,
            "type": latest.block_type,
            "hash": latest.block_hash,
            "timestamp": latest.timestamp,
        }
        if latest
        else None,
        "integrity": {"valid": valid, "error": error},
    }
