"""checksums.py — background MD5 + SHA-256 computation for uploaded files.

After a file lands in its final S3 location, compute both checksums by streaming
the object through Python's hashlib.  Results are stored in the product's
file_metadata under {"checksums": {file_key: {"md5": "...", "sha256": "..."}}}.

Fire-and-forget daemon thread — same pattern as torrent.py and s3_mirror.py.
"""

import hashlib
import logging
import threading

import boto3

log = logging.getLogger(__name__)


def _capture_s3_creds(client):
    creds = client._request_signer._credentials
    meta = client.meta
    return {
        "endpoint": meta.endpoint_url,
        "region": meta.region_name,
        "access_key": creds.access_key,
        "secret_key": creds.secret_key,
    }


def _make_client(creds):
    return boto3.session.Session().client(
        "s3",
        region_name=creds["region"],
        endpoint_url=creds["endpoint"],
        aws_access_key_id=creds["access_key"],
        aws_secret_access_key=creds["secret_key"],
    )


def compute_checksums(s3_client, bucket, s3_key, file_key, product_id, session_factory):
    """Stream s3_key, compute MD5 + SHA-256, save to product file_metadata.

    Designed to run in a background thread.
    """
    log.info("checksums: computing for product=%s key=%s", product_id, s3_key)

    md5 = hashlib.md5()
    sha256 = hashlib.sha256()

    try:
        response = s3_client.get_object(Bucket=bucket, Key=s3_key)
        for chunk in response["Body"].iter_chunks(chunk_size=1024 * 1024):
            md5.update(chunk)
            sha256.update(chunk)
    except Exception:
        log.exception("checksums: failed to stream s3://%s/%s", bucket, s3_key)
        return

    md5_hex = md5.hexdigest()
    sha256_hex = sha256.hexdigest()
    log.info("checksums: product=%s key=%s md5=%s sha256=%s",
             product_id, s3_key, md5_hex, sha256_hex[:16] + "...")

    try:
        with session_factory() as session:
            from ..models.product import Product
            product = session.get(Product, product_id)
            if product is not None:
                product.set_checksum(file_key, md5_hex, sha256_hex)
                session.add(product)
                session.commit()
                log.info("checksums: saved for product=%s file_key=%s", product_id, file_key)
    except Exception:
        log.exception("checksums: failed to save for product=%s", product_id)


def compute_checksums_async(s3_client, bucket, s3_key, file_key, product_id, session_factory):
    """Fire-and-forget: compute checksums in a daemon thread."""
    creds = _capture_s3_creds(s3_client)

    def _run():
        try:
            fresh_client = _make_client(creds)
            compute_checksums(fresh_client, bucket, s3_key, file_key, product_id, session_factory)
        except Exception:
            log.exception("checksums: background thread failed for product=%s", product_id)

    t = threading.Thread(target=_run, daemon=True, name=f"checksums-{product_id}-{file_key}")
    t.start()
    log.info("checksums: background thread started for product=%s file_key=%s", product_id, file_key)
