"""CTCLR-01..10: local-only explicit cleanup/reset; never delete on menu entry."""
import argparse
from datetime import date
import json
import os
from pathlib import Path
import re
import time

import compose_backend as backend
import field_profiles as fp
import server_context as server
from data_console import ask, say, describe, choose
from data_host import Host
from data_safety import atomic, digest, lock, safe


def backups(host):
    rows=[]
    for directory in sorted(host.root.iterdir()):
        if not directory.is_dir() or directory.is_symlink():continue
        marker=directory/'backup-complete.json';payload=directory/'records.backup.json';progress=directory/'progress.json'
        if marker.is_file() and payload.is_file() and not payload.is_symlink():
            done=progress.exists() and json.loads(progress.read_text()).get('complete',False)
            rows.append((payload,done))
    for i,(p,done) in enumerate(rows,1):print(i,p.parent.name,f'{p.stat().st_size/1e6:.1f}MB','complete' if done else 'PROTECTED')
    say('백업은 비공개이며 URI 등 민감정보를 포함할 수 있습니다. 진행 중 작업의 백업은 삭제하지 않습니다.',
        'Backups are private and may contain URI credentials. Incomplete job backups are protected.')
    raw=ask('삭제할 완료 백업 번호 / Enter=조회 종료: ','Completed backup number to delete / Enter=exit: ')
    if not raw:return
    index=int(raw)-1
    if not 0<=index<len(rows) or not rows[index][1]:raise ValueError('BACKUP_PROTECTED')
    p=rows[index][0];stamp=digest(p.read_bytes().hex())
    if ask('이 복구본을 삭제하려면 DELETE BACKUP 입력: ','Type DELETE BACKUP to remove this recovery copy: ')!='DELETE BACKUP':return
    with lock(fp.STATE/'data-maintenance.lock'):
        safe(p)
        if digest(p.read_bytes().hex())!=stamp:raise ValueError('BACKUP_CHANGED')
        p.unlink();atomic(p.parent/'backup-removed.json',dict(removed_at=time.time()))


def main(kind,argv):
    if kind=='storage':
        from field_storage import main as storage
        return storage(argv)
    parser=argparse.ArgumentParser(description='Local maintenance; default scope=all eligible, explicit confirmation required')
    parser.add_argument('action',choices=('clean','reset','backups'))
    parser.add_argument('--camera')
    parser.add_argument('--before',help='KST date YYYY-MM-DD, exclusive')
    parser.add_argument('--no-backup',action='store_true')
    parser.add_argument('--plan',action='store_true',help='Preview only, never stop running analysis')
    parser.add_argument('--resume',help='Owned job directory name; no arbitrary paths')
    args=parser.parse_args(argv)
    expected='reset' if kind in ('db','camera') else 'clean'
    if args.action!=expected and (kind,args.action)!=('data','backups'):parser.error('Invalid action for target')
    if args.before:date.fromisoformat(args.before)
    if args.before and kind in ('camera','redis'):parser.error('Date filtering is not meaningful for current camera/Redis state')
    if args.camera and not re.fullmatch('[A-Za-z0-9_-]{1,100}',args.camera):parser.error('Invalid camera')
    host=Host();server.banner()
    if args.action=='backups':return backups(host)
    paths=host.paths()
    for name,path in paths.items():print(name,path)
    say('미디어 삭제는 복구 불가. Web의 과거 영상 링크가 열리지 않을 수 있습니다. Web DB·인증·모델은 변경하지 않습니다.',
        'Media deletion is irreversible; old Web links may stop working. Web DB/auth/models are preserved.')
    say('AI 카메라 초기화 후 Web Start로 다시 등록될 수 있습니다. 작성·전송·재시도 자료는 보호합니다.',
        'Web Start can recreate reset AI cameras. Writing/delivery/retry jobs remain protected.')
    if not args.plan and host.writers():
        if ask('분석·제어 서비스를 정상 중지하고 계속하려면 STOP 입력: ','Type STOP to stop analysis/control before maintenance: ')!='STOP':return
        backend.runtime('stop')
    if host.writers():raise ValueError('STOP_ANALYSIS_BEFORE_MAINTENANCE')
    with lock(fp.STATE/'data-maintenance.lock'):
        if args.resume:
            if not re.fullmatch(r'job-[0-9]+',args.resume):raise ValueError('JOB_NAME_REQUIRED')
            job=safe(host.root/args.resume);request=fp.read_json(job/'request.json',65536)
            if request['kind']!=kind or request['server']!=server.CURRENT['server']:raise ValueError('JOB_TARGET')
            plan=fp.read_json(job/'plan.json',16*1024**2)
        else:
            job=host.root/('job-'+str(time.time_ns()));job.mkdir(mode=0o700)
            filters={k:v for k,v in dict(camera=args.camera,before=args.before).items() if v}
            if args.camera:
                import yaml
                doc=yaml.safe_load(paths['camera'].read_text())
                filters['stream_ids']=[r['stream_id'] for r in doc.get('added',[]) if str(r.get('camera_id'))==args.camera]
            request=dict(version=1,server=server.CURRENT['server'],kind=kind,filters=filters,no_backup=args.no_backup)
            atomic(job/'request.json',request)
            with host.dependencies(kind):plan=host.run_worker(job,dict(request,operation='plan'))
            atomic(job/'plan.json',plan)
        rows=plan['rows'];describe(rows)
        say(f"합계 {len(rows)}묶음 / 보호 {sum(bool(r['blocked']) for r in rows)} / 예상 {sum(r['bytes'] for r in rows if not r['blocked'])/1e9:.3f}GB",
            f"Total {len(rows)} / protected {sum(bool(r['blocked']) for r in rows)} / eligible bytes {sum(r['bytes'] for r in rows if not r['blocked'])}")
        print('Job:',job,'Resume: ct',kind,args.action,'--resume',job.name)
        if args.plan:return
        if not args.resume:
            choice=ask('1 전체 / 2 개별 선택 / 3 카메라별 / 4 날짜별 / 0 취소 [Enter=1, 아직 삭제 안 함]: ',
                       '1 all / 2 select / 3 camera / 4 date / 0 cancel [Enter=1, no deletion yet]: ') or '1'
            if choice=='0':return
            if choice in ('2','3','4'):
                if choice=='2':plan['rows']=choose(rows)
                elif choice=='3':
                    print('Camera:',', '.join(sorted({str(r.get('camera','')) for r in rows})))
                    camera=ask('카메라 ID / Enter=취소: ','Camera ID / Enter=cancel: ')
                    if not camera:return
                    plan['rows']=[r for r in rows if str(r.get('camera'))==camera]
                else:
                    print('Date:',', '.join(sorted({r.get('date','') for r in rows if r.get('date')})))
                    selected_date=ask('날짜 YYYY-MM-DD / Enter=취소: ','Date YYYY-MM-DD / Enter=cancel: ')
                    if not selected_date:return
                    date.fromisoformat(selected_date)
                    plan['rows']=[r for r in rows if r.get('date')==selected_date]
                plan.pop('hash');plan['hash']=digest(plan);atomic(job/'plan.json',plan)
            elif choice!='1':raise ValueError('SELECTION')
        if not any(not r['blocked'] for r in plan['rows']):
            say('삭제 가능한 대상이 없습니다.','No eligible targets.');return
        eligible=[r for r in plan['rows'] if not r['blocked']]
        say(f"최종 선택: {len(eligible)}묶음 / 미디어 {sum(r['bytes'] for r in eligible)/1e9:.3f}GB / 제외 {len(plan['rows'])-len(eligible)}",
            f"Final selection: {len(eligible)} bundles / media {sum(r['bytes'] for r in eligible)/1e9:.3f}GB / excluded {len(plan['rows'])-len(eligible)}")
        if kind in ('db','camera') and not args.resume:
            if not request['no_backup']:
                answer=ask('1 백업 후 초기화 / 2 이번만 백업 없이 / 0 취소 [Enter=1]: ',
                           '1 backup then reset / 2 no backup this time / 0 cancel [Enter=1]: ') or '1'
                if answer=='0':return
                if answer not in ('1','2'):raise ValueError('BACKUP_SELECTION')
                request['no_backup']=answer=='2';atomic(job/'request.json',request)
        if kind=='db' and any(r['kind'] in ('artifact','training') and r['bytes']>0 and not r['blocked'] for r in plan['rows']):
            say('선택 DB 기록에 연결된 미디어도 함께 삭제됩니다. 보존하려면 취소 후 해당 묶음을 선택에서 제외하세요.',
                'Associated media will ALSO be deleted. Cancel and deselect these bundles to preserve it.')
            if ask('연결 미디어 삭제에도 동의하면 MEDIA 입력: ','Type MEDIA to also delete associated media: ')!='MEDIA':return
        no_backup=kind in ('db','camera') and request['no_backup']
        if no_backup:
            say('백업 없이 기록/설정을 지우면 이전 상태로 복구할 수 없습니다. GPT 질문: AI DB/카메라를 백업 없이 초기화하면 무엇이 사라지나요?',
                'No backup: previous records/settings cannot be recovered. Ask GPT: what is lost when resetting AI DB/cameras without backup?')
        phrase='RESET NO BACKUP' if no_backup else 'RESET' if kind in ('db','camera') else 'DELETE'
        if ask('실행하려면 '+phrase+' 입력 / Enter=취소: ','Type '+phrase+' to execute / Enter=cancel: ')!=phrase:return
        request.update(operation='apply',plan_hash=plan['hash'])
        atomic(job/'consent.json',dict(plan_hash=plan['hash'],no_backup=request['no_backup'],at=time.time()))
        with host.dependencies(kind):result=host.run_worker(job,request)
        atomic(job/'result.json',result)
        print(result)
        say('정리 완료. 분석·카메라 자동 Start 없음. 작업 기록: '+str(job),
            'Complete. No automatic analysis/camera Start. Receipt: '+str(job))
