"""Explicit config-only migration of a pre-fix candidate; never edits the source bundle."""
import argparse
import json
import os
import shutil
from . import models
from .core import absolute, require, read_json, sha256, write_json, atomic_write, lock, now


def repair(source, output, *, apply=False):
    src=absolute(source,no_symlinks=True)
    out=absolute(output,no_symlinks=True)
    require(src!=out and src not in out.parents and out not in src.parents,
            'REPAIR_PATH','원본과 분리된 새 후보 경로를 지정하세요.')
    m=read_json(src/'manifest.json')
    p=m.get('profile',{})
    expected=models.profile(p.get('key'),instances=p.get('instances'),batch=p.get('batch'),workspace=p.get('workspace_mib'))
    require(p==expected and read_json(src/'profile.json')==p,'MODEL_PROFILE','원본 profile 불일치')
    require(m.get('status')=='CONTRACT_PASS' and m.get('smoke')=='NOT_RUN',
            'REPAIR_STATE','기존 변환 계약 PASS, smoke 미완료 후보만 보정합니다.')
    fixed=models.pbtxt(p)
    legacy=fixed.replace('\n]\ninstance_group',',\n]\ninstance_group',1)
    cfg=absolute(src/'config.pbtxt',no_symlinks=True)
    engine=absolute(src/'model.plan',no_symlinks=True)
    require(cfg.stat().st_size<=65536 and cfg.read_text()==legacy,'REPAIR_CONFIG','알려진 마지막 쉼표 오류만 보정합니다.')
    require(sha256(cfg)==m.get('config_sha256') and sha256(engine)==m.get('engine_sha256'),
            'MODEL_HASH','원본 config/engine hash 불일치')
    source_hash=sha256(src/'manifest.json')
    plan={'action':'CONFIG_ONLY_NEW_CANDIDATE','source_manifest_sha256':source_hash,
          'engine_sha256':m['engine_sha256'],'output':str(out),'rebuild':False,'smoke':'NOT_RUN'}
    require(not out.exists(),'FILE_EXISTS','기존 출력은 덮어쓰지 않습니다. 이미 실행했다면 원장을 확인하세요.')
    if not apply:return plan
    with lock(out.parent/('.'+out.name+'.repair.lock')):
        require(not out.exists(),'FILE_EXISTS','다른 작업이 같은 출력 경로를 만들었습니다.')
        out.mkdir(mode=0o700)
        write_json(out/'repair-intent.json',plan,new=True)
        shutil.copyfile(engine,out/'model.plan')
        os.chmod(out/'model.plan',0o644)
        write_json(out/'profile.json',p,new=True)
        atomic_write(out/'config.pbtxt',fixed,mode=0o644,new=True)
        require(sha256(src/'manifest.json')==source_hash and sha256(engine)==m['engine_sha256']
                and sha256(out/'model.plan')==m['engine_sha256'] and sha256(cfg)==m['config_sha256'],
                'SOURCE_CHANGED','복사 중 원본이 변경됐습니다. 부분 산출물은 보존합니다.')
        m['config_sha256']=sha256(out/'config.pbtxt')
        m['config_repair']={'kind':'PBTXT_TRAILING_COMMA','source_manifest_sha256':source_hash,'time':now()}
        m.pop('validation_image_id',None)
        write_json(out/'manifest.json',m,new=True)
        models.candidate_manifest(out)
    return dict(plan,status='REPAIRED_SMOKE_REQUIRED')


def main():
    p=argparse.ArgumentParser(description=__doc__)
    p.add_argument('--source',required=True)
    p.add_argument('--output',required=True)
    p.add_argument('--apply',action='store_true')
    a=p.parse_args()
    print(json.dumps(repair(a.source,a.output),ensure_ascii=False,indent=2))
    if a.apply:
        require(input('변경하려면 APPLY 입력: ')=='APPLY','CANCELLED','취소했습니다.')
        print(json.dumps(repair(a.source,a.output,apply=True),ensure_ascii=False,indent=2))


if __name__=='__main__':main()
