"""Safe projections, not raw logs/env/docker-inspect dumps."""
from __future__ import annotations
import io
import json
from pathlib import Path
import tarfile
from . import host, storage, release
from .core import KitError, absolute, require, canonical, now


def redact(value):
    """Defense in depth only. Arbitrary logs are never added to a support archive."""
    import re
    if isinstance(value,dict):
        return {k:('[REDACTED]' if any(s in k.lower() for s in ('token','secret','password','credential','authorization','private_key')) else redact(v)) for k,v in value.items()}
    if isinstance(value,list): return [redact(v) for v in value]
    if isinstance(value,str):
        value=re.sub(r'(?i)bearer\s+[^\s,;]+','Bearer [REDACTED]',value)
        value=re.sub(r'(?i)(rtsps?|https?)://[^\s/@]+:[^\s/@]+@',r'\1://[REDACTED]@',value)
    return value


def doctor(site=None, *, inv=None):
    inv=host.inventory() if inv is None else inv
    result={'HOST_READY':'UNKNOWN','DEPLOYED':'NOT_CHECKED','RUNTIME_READY':'NOT_RUN','EXTERNAL_READY':'NOT_RUN',
            'inventory':inv,'checks':[]}
    if site is None:
        result['HOST_READY']='PREREQUISITES_ONLY' if inv.get('docker_access')=='PASS' and inv.get('nvidia_runtime') else 'BLOCKED'
        result['note']='site가 없으므로 driver/image/model 요구 조건은 아직 검증하지 않았습니다.'
        return result
    try:
        result['storage']=storage.checks(site)
        host.network_check(site,inv)
        logical={k:Path(e['logical']).exists() and Path(e['physical']).exists() and
                 Path(e['logical']).samefile(e['physical']) for k,e in site['storage'].items()}
        result['logical_views']=logical
        if not all(logical.values()): result['checks'].append({'status':'WARN','code':'LOGICAL_VIEW_MISSING','next':'deployctl storage prepare --apply'})
        current=release.active(site)
        if current:
            from .bundle import verify_directory
            m=verify_directory(Path(site['install_root'])/'releases'/current['release_id'])
            host.enforce_requirements(inv,m.get('requirements',{}))
            result['HOST_READY']='PASS'; result['DEPLOYED']='PASS'; result['release_id']=current['release_id']
            if (release.state_dir(site)/'inflight.json').exists():
                record=release.read_json(release.state_dir(site)/'inflight.json')
                require(record.get('status') in ('DONE','REVIEWED'),'INTERRUPTED_DEPLOYMENT','미완료 배포가 있습니다.')
            result.update(release.health(site,release.snapshot_path(site,current),current['images'],timeout=0))
        else:
            result['DEPLOYED']='NOT_INSTALLED'
            result['HOST_READY']='UNVERIFIED_RELEASE_REQUIREMENTS'
    except (KitError,ValueError,OSError) as exc:
        result['checks'].append({'status':'BLOCKED','code':exc.code if isinstance(exc,KitError) else 'DIAGNOSTIC_UNAVAILABLE'})
    return result


def collect(site, output):
    output=absolute(output,no_symlinks=True)
    require(not output.exists(),'FILE_EXISTS','지원 archive는 새 파일명으로 저장하세요.')
    report={'generated_at':now(),'scope':'SAFE_PROJECTIONS_ONLY','inventory':host.inventory(),
            'excluded':['raw logs','env content','tokens','private keys','DB','RTSP URI','full inspect','full Compose']}
    if site:
        report['site']={k:site[k] for k in ('install_root','data_root','ai_ip','project','required_models')}
        report['storage']={k:{x:e[x] for x in ('physical','logical','container','expected')} for k,e in site['storage'].items()}
        try:
            a=release.active(site)
            report['release']=None if a is None else {k:a[k] for k in ('release_id','transaction','images','compatibility_id','archive_sha256')}
            report['containers']=release.containers(site['project'])
            pending=release.state_dir(site)/'inflight.json'
            if pending.exists():
                record=release.read_json(pending)
                report['last_operation']={k:record[k] for k in
                    ('transaction','release_id','status','stage','time') if k in record}
                failure=record.get('failure',{})
                report['last_operation']['failure']={k:failure[k] for k in
                    ('code','exception_type','diagnostic') if k in failure}
        except (KitError,OSError,ValueError): report['containers']='UNAVAILABLE_OR_SUDO_REQUIRED'
    raw=canonical(redact(report))
    output.parent.mkdir(parents=True,exist_ok=True)
    with output.open('xb') as f, tarfile.open(fileobj=f,mode='w:gz') as tar:
        import os
        os.fchmod(f.fileno(),0o600)
        info=tarfile.TarInfo('support.json'); info.size=len(raw); info.mode=0o600
        tar.addfile(info,io.BytesIO(raw))
    return {'archive':str(output),'contains_secrets':False,'raw_logs':'INTENTIONALLY_EXCLUDED'}
