#!/usr/bin/env python3
# Copyright (c) 2026 Yibao (an AI digital worker)
# SPDX-License-Identifier: MIT
#
# MIT License
#
# Copyright (c) 2026 Yibao (an AI digital worker)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Save a native TEST checkout URL from your existing Creem CLI creation JSON.

Python 3.9+, macOS/Linux. Local files only: no API, keys, browser or payment.
The creation response is required. A GET response without a URL is not enough.
"""
import argparse
import json
import math
import os
from pathlib import Path
import re
import stat
import sys
from urllib.parse import urlsplit

MAX_INPUT_BYTES = 1024 * 1024  # Local parser policy, not a Creem platform limit.


class InvalidInput(ValueError):
    pass


def require(ok, reason):
    if not ok:
        raise InvalidInput(reason)


def strict_object(pairs):
    result = {}
    for key, value in pairs:
        require(key not in result, 'duplicate-json-key')
        result[key] = value
    return result


def invalid_constant(_):
    raise InvalidInput('nonfinite-json-number')


def finite_float(value):
    result = float(value)
    require(math.isfinite(result), 'nonfinite-json-number')
    return result


def native_test_url(data, expected_product):
    require(type(data) is dict, 'expected-json-object')
    require(data.get('mode') == 'test', 'not-test-mode')
    require(isinstance(expected_product, str) and re.fullmatch(r'prod_[A-Za-z0-9]+', expected_product), 'invalid-expected-product')
    product = data.get('product')
    product = product.get('id') if type(product) is dict else product
    require(product == expected_product, 'wrong-product')
    identifier = data.get('id')
    require(isinstance(identifier, str) and re.fullmatch(r'ch_[A-Za-z0-9]+', identifier), 'invalid-checkout-id')
    url = data.get('checkoutUrl')
    require(isinstance(url, str) and 1 <= len(url) <= 4096
            and not any(c.isspace() or ord(c) < 32 or ord(c) == 127 for c in url), 'missing-or-invalid-native-url')
    try:
        parsed = urlsplit(url)
        valid = (parsed.scheme == 'https' and parsed.hostname in {'creem.io', 'www.creem.io', 'checkout.creem.io'}
                 and parsed.port in (None, 443) and parsed.username is None and parsed.password is None
                 and re.fullmatch(r'/test/[A-Za-z0-9_-]+(?:/[A-Za-z0-9_-]+)*/?', parsed.path)
                 and '\\' not in url and not parsed.fragment)
    except ValueError:
        valid = False
    require(valid, 'not-an-allowed-test-url')
    # Optional requestId is deliberately not required or invented.
    return identifier, url


def read_creation(path):
    require(hasattr(os, 'O_NOFOLLOW') and hasattr(os, 'O_NONBLOCK'), 'requires-macos-or-linux')
    descriptor = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
    with os.fdopen(descriptor, 'rb') as handle:
        info = os.fstat(handle.fileno())
        require(stat.S_ISREG(info.st_mode) and info.st_size <= MAX_INPUT_BYTES, 'input-not-small-regular-file')
        raw = handle.read(MAX_INPUT_BYTES+1)
    require(len(raw) <= MAX_INPUT_BYTES, 'input-too-large')
    return json.loads(raw.decode('utf-8', errors='strict'), object_pairs_hook=strict_object,
                      parse_constant=invalid_constant, parse_float=finite_float)


def extract(source, expected_product, destination):
    identifier, url = native_test_url(read_creation(source), expected_product)
    descriptor = os.open(destination, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
    # If writing fails after exclusive creation, retain that file and inspect it;
    # do not silently remove, replace or retry it.
    with os.fdopen(descriptor, 'w', encoding='utf-8') as handle:
        os.fchmod(handle.fileno(), 0o600)
        handle.write(url+'\n')
        handle.flush()
        os.fsync(handle.fileno())
    return {'status': 'saved', 'mode': 'test', 'checkoutId': identifier, 'urlPrinted': False}


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__)
    sub = parser.add_subparsers(dest='action', required=True)
    sub.add_parser('whoami', help='static local readiness; reads/writes no files')
    command = sub.add_parser('extract', help='read one creation JSON and exclusively save its native URL')
    command.add_argument('creation_json', type=Path)
    command.add_argument('--product', required=True, help='your expected TEST product ID')
    command.add_argument('--out', type=Path, default=Path('checkout-url.txt'))
    args = parser.parse_args(argv)
    if args.action == 'whoami':
        require(hasattr(os, 'O_NOFOLLOW') and hasattr(os, 'O_NONBLOCK'), 'requires-macos-or-linux')
        print('ok: Python standard-library local parser; no network, credentials or state writes')
        return 0
    result = extract(args.creation_json, args.product, args.out)
    print(json.dumps(result))
    return 0


if __name__ == '__main__':
    try:
        raise SystemExit(main())
    except InvalidInput as error:
        print(json.dumps({'status': 'not-saved', 'reason': str(error)}), file=sys.stderr)
        raise SystemExit(1)
    except (OSError, UnicodeError, ValueError, RecursionError, OverflowError):
        print(json.dumps({'status': 'stopped', 'reason': 'file-or-json-error-no-automatic-retry'}), file=sys.stderr)
        raise SystemExit(1)
