"""Optional large-file transport. Existing USB files are never replaced."""
import hashlib
from pathlib import Path

from ..core import absolute, identifier, read_json, relative, require, sha256, write_json


def split(source, directory, size=2*1024**3):
    source=absolute(source,no_symlinks=True);directory=absolute(directory,no_symlinks=True)
    require(source.is_file() and type(size) is int and size>0,'SPLIT_INPUT','파일과 분할 크기를 확인하세요.')
    require(not directory.exists(),'OUTPUT_EXISTS','기존 분할 폴더는 보존합니다.')
    directory.mkdir(parents=True)
    parts=[];whole=hashlib.sha256()
    with source.open('rb') as src:
        while True:
            first=src.read(min(size,1024**2))
            if not first:break
            name=f'part-{len(parts):05d}';h=hashlib.sha256();count=0
            with (directory/name).open('xb') as out:
                block=first
                while block:
                    out.write(block);h.update(block);whole.update(block);count+=len(block)
                    if count>=size:break
                    block=src.read(min(1024**2,size-count))
            parts.append(dict(name=name,size=count,sha256=h.hexdigest()))
    write_json(directory/'parts.json',dict(schema=1,name=source.name,size=sum(p['size'] for p in parts),
              sha256=whole.hexdigest(),parts=parts),new=True,mode=0o644)
    return sha256(directory/'parts.json')


def join(directory, output, expected):
    directory=absolute(directory,no_symlinks=True);output=absolute(output,no_symlinks=True)
    require(sha256(directory/'parts.json')==expected,'PARTS_HASH','승인된 분할 목록과 다릅니다.')
    m=read_json(directory/'parts.json');require(m.get('schema')==1 and len(m['parts'])<=1024,'PARTS_SCHEMA','분할 목록을 확인하세요.')
    require(not output.exists(),'OUTPUT_EXISTS','기존 파일을 덮어쓰지 않습니다.')
    names=[p['name'] for p in m['parts']]
    require(names==[f'part-{i:05d}' for i in range(len(names))],'PART_ORDER','조각 순서/중복 오류입니다.')
    for p in m['parts']:
        src=absolute(directory/relative(p['name']),no_symlinks=True)
        require(src.stat().st_size==p['size'] and sha256(src)==p['sha256'],'PART_HASH','조각이 누락되거나 손상됐습니다.')
    output.parent.mkdir(parents=True,exist_ok=True)
    with output.open('xb') as out:
        for p in m['parts']:
            with (directory/p['name']).open('rb') as src:
                for b in iter(lambda:src.read(1024**2),b''):out.write(b)
    require(output.stat().st_size==m['size'] and sha256(output)==m['sha256'],'JOIN_HASH','합친 파일이 원본과 다릅니다. 부분 파일은 보존합니다.')
    return output
