"""Register a custom version of a library asset (repaint, recolour, shape edit, resize, rig change, re-run, other author).

usage: python3 tools/register_variant.py <parent-id> <file.glb> --kind retexture --note "why" [--name N] [--suffix S]
                                        [--thumbnail img.jpg] [--no-reuse] [--author A] [--status candidate] [--no-enrich]

ID RULE: a variant is a NEW ASSET ID `<parent-class>/<parent-slug>-<suffix>` (suffix = --suffix, else the kind; `-2`, `-3`
when taken), version 1, status candidate. Why not a new version of the parent: a new version replaces what games get for
the parent (the active version), and a custom look must never swap under a game that pinned the original. Same-id versions
stay for fixes and lighter copies of the SAME thing (tools/rerun.py, tools/lighter.py).
Lineage lives in provenance.parent {id, version, release} + provenance.variant {kind, note} (outside the release hash).
The variant gets its own blob, thumbnail, description and vectors; nothing is copied from the parent except what the kind
keeps true (retexture/recolor keep the parent's frame, sockets, nodes, rig). Reuse is the parent's AND the variant's
(tools/build_index.py link_variants); --no-reuse blocks it outright.
Enrich = Gemini description + vectors for this one asset. Needs a thumbnail (--thumbnail; the GPU pass on gex44 is not run
from here). Without one, or if Vertex fails, the asset is listed as PENDING and the next `tools/enrich_library.py` picks it up.
"""
import argparse, copy, hashlib, json, os, shutil, subprocess, sys, time
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(ROOT, 'lib')); sys.path.insert(0, os.path.join(ROOT, 'tools'))
import glb as glblib, card as cardlib  # noqa: E402
from import_existing import build_card  # noqa: E402

KEEP_FRAME = ('retexture', 'recolor')  # same mesh and nodes: the parent's sockets, nodes, rig and frame stay true


def new_id(allc, parent_id, suffix):
    cls, slug = parent_id.split('/')
    cand, n = f'{cls}/{slug}-{suffix}', 1
    while cand in allc:
        n += 1
        cand = f'{cls}/{slug}-{suffix}-{n}'
    return cand


def register(parent_id, file, kind, note, name=None, suffix=None, no_reuse=False, thumbnail=None, author=None,
             status='candidate', enrich=True, root=ROOT):
    if kind not in cardlib.VARIANT_KINDS:
        raise ValueError(f'kind must be one of {cardlib.VARIANT_KINDS}')
    allc = cardlib.load_all(root)
    if parent_id not in allc:
        raise ValueError(f'unknown parent {parent_id}')
    parent = cardlib.active(allc[parent_id])
    cid = new_id(allc, parent_id, suffix or kind)
    cls, slug = cid.split('/')
    m = glblib.measure(file)
    h = hashlib.sha256(open(file, 'rb').read()).hexdigest()
    os.makedirs(f'{root}/store/blobs', exist_ok=True)
    shutil.copyfile(file, f'{root}/store/blobs/{h}.glb')
    parent_ref = {'id': parent_id, 'version': parent['version'], 'release': parent['release']}
    s = dict(file=file, slug=slug, path=cls, name=name or f"{parent['name']} ({kind})", version=1,
             summary=f"{kind} of {parent['name']}: {note}"[:300], style=parent['style'], frame=parent['frame']['contract'],
             gates='variant: measured from the file, not gated', status=status,
             licence=dict(parent['licence'], evidence=f"{parent['licence']['evidence']} | variant ({kind}) of {parent_id} v{parent['version']}: {note}"),
             provenance=dict(author=author or f'custom {kind} of {parent_id}', source=f'variant of {parent_id}', kit='variant', job=None,
                             feedback=note, parent=parent_ref, variant={'kind': kind, 'note': note}))
    c = build_card(s, m, h)
    c['provenance']['imported'] = time.strftime('%Y-%m-%d')
    if kind in KEEP_FRAME:
        for k in ('frame', 'sockets', 'nodes', 'placement', 'gameplay', 'rig', 'animations', 'paint'):
            c[k] = copy.deepcopy(parent.get(k))
    c['tags'] = sorted(set(c['tags']) | set(parent.get('tags') or []) | {kind})
    if no_reuse:
        c['reuse'] = {'omg': False, 'omu': False, 'reason': f'variant registered with --no-reuse ({note})'}
    thumb_ok = False
    if thumbnail:
        d = f'{root}/media/{h}'
        os.makedirs(d, exist_ok=True)
        th = hashlib.sha256(open(thumbnail, 'rb').read()).hexdigest()[:16]
        shutil.copyfile(thumbnail, f'{d}/thumbnail-{th}.jpg')
        c['thumbnail'] = f'media/{h}/thumbnail-{th}.jpg'
        thumb_ok = True
    c['release'] = cardlib.release_hash(c)
    path = cardlib.write(root, c)

    def run(*a):
        return subprocess.run([sys.executable, *a], cwd=root, capture_output=True, text=True)
    r = run('tools/build_index.py')
    if r.returncode:
        raise RuntimeError('index build failed: ' + r.stderr[-400:])
    enriched = 'pending: no thumbnail given (pass --thumbnail)'
    if enrich and thumb_ok:
        r = run('tools/enrich_library.py', '--only', cid)
        if r.returncode:
            enriched = 'pending: Vertex/enrich failed: ' + (r.stderr or r.stdout)[-300:].strip()
        else:
            enriched = 'done'
            r = run('tools/build_index.py')
            if r.returncode:
                raise RuntimeError('index rebuild failed: ' + r.stderr[-400:])
    elif not enrich:
        enriched = 'pending: --no-enrich'
    return {'id': cid, 'card': path, 'blob': h, 'enrich': enriched, 'parent': parent_ref}


def main():
    p = argparse.ArgumentParser(description=__doc__.split('\n')[0])
    p.add_argument('parent'); p.add_argument('glb'); p.add_argument('--kind', required=True, choices=cardlib.VARIANT_KINDS)
    p.add_argument('--note', required=True); p.add_argument('--name'); p.add_argument('--suffix'); p.add_argument('--thumbnail')
    p.add_argument('--no-reuse', action='store_true'); p.add_argument('--author'); p.add_argument('--status', default='candidate')
    p.add_argument('--no-enrich', action='store_true')
    a = p.parse_args()
    r = register(a.parent, os.path.abspath(a.glb), a.kind, a.note, a.name, a.suffix, a.no_reuse,
                 os.path.abspath(a.thumbnail) if a.thumbnail else None, a.author, a.status, not a.no_enrich)
    print(json.dumps(r, indent=1))
    if r['enrich'] != 'done':
        print('ENRICH ' + r['enrich'].upper(), file=sys.stderr)


if __name__ == '__main__':
    main()
