import argparse
from .core import uBase, abc, effBase

VERSION = "0.2.0"

def asHex(s):
    return ' '.join(format(ord(c), 'x') for c in s)

def asDigits(a):
    return ' '.join(str(v) for v in a)

def show(v):
    if isinstance(v, list):
        return asDigits(v)
    return str(v)

def getSafe(a):
    return 0 if getattr(a, "unsafe", False) else getattr(a, "safe", 1)

def getX(a):
    x = []

    seed = getattr(a, "seed", None) or []
    exclude = getattr(a, "exclude", None) or []
    rangeIds = getattr(a, "rangeIds", None) or []
    spec = getattr(a, "spec", None) or []

    if seed:
        x.append(-1)
        x.extend(seed)

    for v in exclude:
        x.append(("x", v))

    for v in rangeIds:
        x.append(v)

    for v in spec:
        try:
            x.append(int(v))
        except:
            x.append(("x", v))

    return x or None

def getCfg(a):
    return getX(a), getattr(a, "unicode", 0), getSafe(a)

def parseNum(s):
    return int(s, 0)

def addCfg(p):
    p.add_argument("-m", "--mode", type=int, default=0, help="mode: 0 symbols, 1 codepoint hex, 2 digit indices")
    p.add_argument("-u", "--unicode", type=int, default=0, help="unicode mode: 0 curated, 1 full unicode order")
    p.add_argument("--safe", type=int, default=1, help="safe filter: 1 on, 0 off")
    p.add_argument("--unsafe", action="store_true", help="shortcut for --safe 0")

    p.add_argument("--seed", action="append", default=[], help="custom alphabet seed segment, kept in given order; repeatable")
    p.add_argument("--exclude", action="append", default=[], help="exclude characters from the alphabet; repeatable")
    p.add_argument("--range", dest="rangeIds", action="append", type=int, default=[], help="exclude unicode range id; repeatable")
    p.add_argument("-x", "--spec", nargs="*", default=[], help="legacy mixed exclusion spec: strings exclude chars, ints exclude range ids")

def printCfg(a):
    x, u, safe = getCfg(a)
    print(f"requestedBase: {a.b}")
    print(f"effectiveBase: {effBase(a.b, x, u, safe)}")
    print(f"s: {getattr(a, 's', 'n/a')}")
    print(f"m: {getattr(a, 'mode', 'n/a')}")
    print(f"u: {u}")
    print(f"safe: {safe}")
    print(f"x: {x}")

def runEncode(a):
    x, u, safe = getCfg(a)
    n = parseNum(a.n)
    print(show(uBase(n, a.b, a.s, a.mode, x, u, safe)))

def runDecode(a):
    x, u, safe = getCfg(a)
    print(uBase(a.n, a.b, a.s, a.mode, x, u, safe))

def runAlphabet(a):
    x, u, safe = getCfg(a)
    eff = effBase(a.b, x, u, safe)
    out = abc(a.b, x, u, safe)

    if not a.full:
        out = out[:a.limit]

    if a.hex_view:
        out = asHex(out)

    print(out)

    if not a.full:
        shown = min(a.limit, eff)
        print()
        print(f"shown: {shown}")
        print(f"effectiveBase: {eff}")

def runEff(a):
    x, u, safe = getCfg(a)
    print(effBase(a.b, x, u, safe))

def runInfo(a):
    x, u, safe = getCfg(a)
    eff = effBase(a.b, x, u, safe)
    out = abc(a.b, x, u, safe)
    prev = out[:a.limit]
    printCfg(a)
    print(f"alphabetPreview: {prev}")
    print(f"alphabetPreviewHex: {asHex(prev)}")
    print(f"previewCount: {len(prev)}")
    print(f"totalCount: {eff}")

def runRound(a):
    x, u, safe = getCfg(a)
    n = parseNum(a.n)

    glyph = uBase(n, a.b, a.s, 0, x, u, safe)
    hexv = uBase(n, a.b, a.s, 1, x, u, safe)
    digs = uBase(n, a.b, a.s, 2, x, u, safe)

    back0 = uBase(glyph, a.b, a.s, 0, x, u, safe)
    back1 = uBase(hexv, a.b, a.s, 1, x, u, safe)
    back2 = uBase(digs, a.b, a.s, 2, x, u, safe)

    ok = back0 == n and back1 == n and back2 == n

    printCfg(a)
    print(f"input: {n}")
    print(f"glyph: {glyph}")
    print(f"hex: {hexv}")
    print(f"digits: {asDigits(digs)}")
    print(f"glyphToInt: {back0}")
    print(f"hexToInt: {back1}")
    print(f"digitsToInt: {back2}")
    print(f"status: {'PASS' if ok else 'FAIL'}")

    raise SystemExit(0 if ok else 1)

def main():
    p = argparse.ArgumentParser(
        prog="ubase-core",
        description="Universal base conversion with seeded alphabets, unicode filtering, and round-trip inspection."
    )
    p.add_argument("--version", action="version", version=f"ubase-core {VERSION}")

    sub = p.add_subparsers(dest="cmd", required=True)

    cfg = argparse.ArgumentParser(add_help=False)
    addCfg(cfg)

    pe = sub.add_parser("encode", parents=[cfg], help="encode an integer")
    pe.add_argument("n", help="integer input; decimal by default, or 0x/0o/0b prefixed")
    pe.add_argument("b", type=int, help="requested base; 0=base62+emoji reservoir, 1=emoji reservoir")
    pe.add_argument("s", type=int, nargs="?", default=1, help="significance mode; invalid values fall back to 1")
    pe.set_defaults(run=runEncode)

    pd = sub.add_parser("decode", parents=[cfg], help="decode symbols, hex view, or digit indices back to an integer")
    pd.add_argument("n", help="input string; for mode 2 use quoted space or comma separated digits")
    pd.add_argument("b", type=int, help="requested base; 0=base62+emoji reservoir, 1=emoji reservoir")
    pd.add_argument("s", type=int, nargs="?", default=1, help="significance mode; invalid values fall back to 1")
    pd.set_defaults(run=runDecode)

    pa = sub.add_parser("alphabet", parents=[cfg], help="show the resolved alphabet or a preview of it")
    pa.add_argument("b", type=int, help="requested base")
    pa.add_argument("--limit", type=int, default=128, help="preview length when not using --full")
    pa.add_argument("--full", action="store_true", help="print the full resolved alphabet")
    pa.add_argument("--hex-view", action="store_true", help="print alphabet as space separated codepoint hex")
    pa.set_defaults(run=runAlphabet)

    pf = sub.add_parser("effbase", parents=[cfg], help="show the effective base after clamping and filtering")
    pf.add_argument("b", type=int, help="requested base")
    pf.set_defaults(run=runEff)

    pi = sub.add_parser("info", parents=[cfg], help="show resolved configuration and alphabet preview")
    pi.add_argument("b", type=int, help="requested base")
    pi.add_argument("--limit", type=int, default=64, help="alphabet preview length")
    pi.set_defaults(run=runInfo)

    pr = sub.add_parser("roundtrip", parents=[cfg], help="encode in all modes and verify full round trip")
    pr.add_argument("n", help="integer input; decimal by default, or 0x/0o/0b prefixed")
    pr.add_argument("b", type=int, help="requested base")
    pr.add_argument("s", type=int, nargs="?", default=1, help="significance mode; invalid values fall back to 1")
    pr.set_defaults(run=runRound)

    a = p.parse_args()
    a.run(a)

if __name__ == "__main__":
    main()