"""Strict original intake provenance and private restore of visitor photos."""
from datetime import datetime
import json
import re
import sqlite3
import uuid
from zoneinfo import ZoneInfo

from .public_enquiries import EnquiryFacts, EnquiryPhoto, PUBLIC_ACTOR, MAX_FILE_BYTES, MAX_TOTAL_BYTES, MAX_NORMALIZED_BYTES, content_digest


def _non_member_author(db, table, row):
    if row['membership_id'] != '': return False
    actor = row['uploaded_by'] if table == 'job_photos' else row['actor_id']
    if table in ('job_photos', 'photo_history'):
        pid = row['id'] if table == 'job_photos' else row['photo_id']
        linked = db.execute('SELECT 1 FROM enquiry_photo_sources s JOIN enquiry_intakes i ON i.job_id=s.job_id JOIN job_photos p ON p.id=s.photo_id AND p.job_id=s.job_id WHERE s.photo_id=?', (pid,)).fetchone()
        if not linked: return False
        if table == 'job_photos': return actor == PUBLIC_ACTOR
        if actor == PUBLIC_ACTOR: return row['action'] == 'photo.uploaded' and json.loads(row['snapshot'])['version'] == 1
        snap = json.loads(row['snapshot'])
        return actor == 'recovery' and row['action'] == 'photo.enquiry_recovery_suspended' and not snap['reviewed'] and snap['customer_share'] is None
    if table == 'gallery_history' and actor == 'recovery' and row['action'] == 'project.enquiry_recovery_withdraw':
        snap = json.loads(row['snapshot'])
        return snap['published'] is None and db.execute('SELECT 1 FROM enquiry_photo_sources WHERE job_id=?', (snap['source_job_id'],)).fetchone() is not None
    return False


def non_member_author(db, table, row):
    try: return _non_member_author(db, table, row)
    except (KeyError, TypeError, ValueError): return False


def validate(db, require):
    previous = db.row_factory
    db.row_factory = sqlite3.Row
    try:
        rows = lambda table: [dict(r) for r in db.execute('SELECT * FROM ' + table)]
        jobs = {r['id']: r for r in rows('jobs')}
        photos = {r['id']: r for r in rows('job_photos')}
        blobs = {r['id']: r for r in rows('photo_blobs')}
        intakes = {r['job_id']: r for r in rows('enquiry_intakes')}
        sources = {r['photo_id']: r for r in rows('enquiry_photo_sources')}
        require(set(sources) == {p['id'] for p in photos.values() if p['uploaded_by'] == PUBLIC_ACTOR}, 'Visitor photo provenance is incomplete.')
        events = [r['subject'] for r in rows('audit') if r['action'] == 'enquiry.public_submitted']
        require(set(events) == set(intakes) and len(events) == len(intakes), 'Original enquiry audit provenance is incomplete.')
        require(all(type(p['position']) is int and type(p['source_bytes']) is int for p in sources.values()), 'Invalid enquiry source numbers.')
        from .customer_review import historical_owner
        for jid, intake in intakes.items():
            require(jid in jobs and jobs[jid]['source'] == 'website' and historical_owner(db, jid, intake['customer_id']), 'Original enquiry customer/job binding is invalid.')
            require(intake['created_at'] == jobs[jid]['created_at'], 'Original enquiry creation time changed.')
            raw = json.loads(intake['submitted'])
            facts = EnquiryFacts.model_validate(raw).model_dump()
            require(raw == facts, 'Original enquiry facts are not canonical.')
            stamp = datetime.fromisoformat(intake['created_at'])
            require(stamp.tzinfo is not None, 'Original enquiry time needs a timezone.')
            require(not facts['preferred_date'] or facts['preferred_date'] >= stamp.astimezone(ZoneInfo('Europe/London')).date().isoformat(), 'Original enquiry preferred date predates its submission.')
            attached = sorted((p for p in sources.values() if p['job_id'] == jid), key=lambda p: p['position'])
            require(len(attached) <= 5 and [p['position'] for p in attached] == list(range(len(attached))), 'Original enquiry photo order/count is invalid.')
            require(sum(p['source_bytes'] for p in attached) <= MAX_TOTAL_BYTES, 'Original enquiry files exceed the total limit.')
            require(sum(blobs[photos[p['photo_id']]['blob_id']]['bytes'] for p in attached) <= MAX_NORMALIZED_BYTES, 'Normalized enquiry files exceed the total limit.')
            require(str(uuid.UUID(intake['operation_key'])) == intake['operation_key'], 'Original enquiry retry identity is invalid.')
            require(content_digest(facts, [{k:p[k] for k in ('filename','source_bytes','source_sha256')} for p in attached]) == intake['content_sha256'], 'Original enquiry facts/photo provenance checksum changed.')
        for pid, source in sources.items():
            require(source['job_id'] in intakes and pid in photos, 'Enquiry photo source is missing.')
            photo = photos[pid]
            require(photo['job_id'] == source['job_id'] and photo['membership_id'] == '' and source['created_at'] == photo['created_at'] == intakes[source['job_id']]['created_at'], 'Enquiry photo changed its original binding.')
            require(type(source['source_bytes']) is int and 0 < source['source_bytes'] <= MAX_FILE_BYTES and source['source_format'] in ('PNG', 'JPEG', 'WEBP', 'HEIF'), 'Enquiry photo source format/size is invalid.')
            require(isinstance(source['source_sha256'], str) and re.fullmatch('[a-f0-9]{64}', source['source_sha256']) is not None, 'Enquiry photo source checksum is invalid.')
            require(EnquiryPhoto(filename=source['filename'], data='AAAA').filename == source['filename'], 'Enquiry photo filename is not canonical.')
            first = db.execute('SELECT * FROM photo_history WHERE photo_id=? ORDER BY id LIMIT 1', (pid,)).fetchone()
            require(first is not None and first['action'] == 'photo.uploaded' and first['actor_id'] == PUBLIC_ACTOR and first['membership_id'] == '', 'Enquiry photo original upload event is missing.')
            snap = json.loads(first['snapshot'])
            require(snap['customer_id'] == intakes[source['job_id']]['customer_id'] and snap['position'] == source['position'] and snap['reviewed'] == 0 and snap['archived'] == 0 and snap['customer_share'] is None and snap['version'] == 1, 'Visitor upload cannot invent sharing permission or a different original customer.')
    except (KeyError, TypeError, ValueError):
        require(False, 'Original enquiry provenance failed validation.')
    finally:
        db.row_factory = previous


def restored(store):
    """Preserve originals/history, but renew owner review before further use."""
    from .photos import photo_snapshot, project_snapshot
    from .store import many, now
    actor = {'subject': 'recovery', 'membership_id': ''}
    with store.registry() as registry:
        businesses = many(registry, 'SELECT id FROM businesses')
    for business in businesses:
        with store.tenant(business['id']) as db:
            ids = {p['photo_id'] for p in many(db, 'SELECT photo_id FROM enquiry_photo_sources')}
            for project in many(db, 'SELECT * FROM gallery_projects WHERE published IS NOT NULL'):
                if any(i['source_kind'] == 'job' and i['source_id'] in ids for i in json.loads(project['published'])['images']):
                    db.execute('UPDATE gallery_projects SET published=NULL,version=version+1,updated_at=? WHERE id=?', (now(), project['id']))
                    project_snapshot(db, project['id'], actor, 'project.enquiry_recovery_withdraw')
            for pid in ids:
                row = db.execute('SELECT reviewed,customer_share FROM job_photos WHERE id=?', (pid,)).fetchone()
                if row['reviewed'] or row['customer_share'] is not None:
                    db.execute('UPDATE job_photos SET reviewed=0,customer_share=NULL,version=version+1,updated_at=? WHERE id=?', (now(), pid))
                    photo_snapshot(db, pid, actor, 'photo.enquiry_recovery_suspended')
