"""Local ownership checks against fictional JSON; no BigCommerce or ERP call."""
from dataclasses import dataclass
from pathlib import Path
from tempfile import TemporaryDirectory
import copy
import json
import sqlite3

if sqlite3.sqlite_version_info < (3, 24, 0):
    raise RuntimeError('SQLite 3.24 or newer required')


class OwnershipError(ValueError):
    pass


@dataclass(frozen=True)
class ExpectedRefund:
    store_hash: str
    order_id: int
    refund_id: int


def positive_fixture_id(value):
    # Deliberate local policy: no bool, numeric string, zero or implicit coercion.
    if type(value) is not int or value <= 0:
        raise OwnershipError('invalid positive integer identity')
    return value


def expected_from_event(trusted_subscription_store, event):
    # The trusted subscription context is supplied separately; this does not authenticate a webhook.
    if not isinstance(trusted_subscription_store, str) or not trusted_subscription_store.strip():
        raise OwnershipError('missing trusted store context')
    try:
        if event['scope'] != 'store/order/refund/created' or event['data']['type'] != 'order':
            raise OwnershipError('unexpected event')
        return ExpectedRefund(
            trusted_subscription_store,
            positive_fixture_id(event['data']['id']),
            positive_fixture_id(event['data']['refund']['refund_id']),
        )
    except (KeyError, TypeError) as error:
        raise OwnershipError('incomplete event identity') from error


def verify_read(expected, actual_client_store, http_status, payload):
    # actual_client_store must describe the client that performed the read, not a payload field.
    if not isinstance(expected.store_hash, str) or not expected.store_hash.strip() or actual_client_store != expected.store_hash:
        raise OwnershipError('wrong API client store')
    positive_fixture_id(expected.order_id)
    positive_fixture_id(expected.refund_id)
    if http_status != 200:
        raise OwnershipError('read not confirmed')
    try:
        data = payload['data']
        refund_id = positive_fixture_id(data['id'])
        order_id = positive_fixture_id(data['order_id'])
    except (KeyError, TypeError) as error:
        raise OwnershipError('incomplete response identity') from error
    if refund_id != expected.refund_id or order_id != expected.order_id:
        raise OwnershipError('refund does not belong to expected order/refund pair')
    return expected  # Local verified identity only; no payment/accounting authorization.


def record_candidate(database, verified):
    with sqlite3.connect(database) as connection:
        connection.execute(
            'INSERT INTO candidates VALUES (?,?,?) ON CONFLICT(store_hash,refund_id) DO NOTHING',
            (verified.store_hash, verified.refund_id, verified.order_id),
        )
        order_id = connection.execute(
            'SELECT order_id FROM candidates WHERE store_hash=? AND refund_id=?',
            (verified.store_hash, verified.refund_id),
        ).fetchone()[0]
        if order_id != verified.order_id:
            raise OwnershipError('stored ownership contradiction')
    return (verified.store_hash, verified.refund_id, order_id)


checks = 0


def check(condition, label):
    global checks
    if not condition:
        raise AssertionError(label)
    checks += 1
    print('PASS', label)


def rejects(action, label):
    try:
        action()
    except OwnershipError:
        check(True, label)
    else:
        raise AssertionError(label)


event = {'scope': 'store/order/refund/created', 'data': {'type': 'order', 'id': 900, 'refund': {'refund_id': 77}}}
expected = expected_from_event('store-a-fixture', event)
check(expected.order_id == 900 and expected.refund_id == 77, 'event order and refund identities extracted separately')
response = {'data': {'id': 77, 'order_id': 900}}
verified = verify_read(expected, 'store-a-fixture', 200, response)
check(verified == expected, 'matching response identity accepted as candidate only')
wrong_order = ExpectedRefund('store-a-fixture', 901, 77)
rejects(lambda: verify_read(wrong_order, 'store-a-fixture', 200, response), 'HTTP 200 does not authorize cross-order association')
rejects(lambda: verify_read(ExpectedRefund('store-a-fixture', 900, 78), 'store-a-fixture', 200, response), 'wrong refund id rejected')
rejects(lambda: verify_read(expected, 'store-b-fixture', 200, response), 'wrong actual client store rejected')
spoofed = copy.deepcopy(response)
spoofed['store_hash'] = expected.store_hash
rejects(lambda: verify_read(expected, 'store-b-fixture', 200, spoofed), 'invented payload store field cannot override client context')
for field in ['id', 'order_id']:
    missing = copy.deepcopy(response)
    del missing['data'][field]
    rejects(lambda missing=missing: verify_read(expected, 'store-a-fixture', 200, missing), 'missing response '+field+' rejected')
for value in [True, '77', 0, None]:
    malformed = {'data': {'id': value, 'order_id': 900}}
    rejects(lambda malformed=malformed: verify_read(expected, 'store-a-fixture', 200, malformed), 'non-conforming refund identity '+repr(value)+' rejected')
for status in [404, 429, 500]:
    rejects(lambda status=status: verify_read(expected, 'store-a-fixture', status, response), 'HTTP '+str(status)+' not treated as confirmed read')
rejects(lambda: verify_read(expected, 'store-a-fixture', 200, {}), 'missing response envelope rejected')
rejects(lambda: verify_read(expected, 'store-a-fixture', 200, {'data': []}), 'wrong envelope type rejected')
rejects(lambda: expected_from_event('', event), 'missing trusted subscription context rejected')
other_event = copy.deepcopy(event)
other_event['scope'] = 'store/order/created'
rejects(lambda: expected_from_event('store-a-fixture', other_event), 'order-created notification is not a refund event')
incomplete = copy.deepcopy(event)
del incomplete['data']['refund']
rejects(lambda: expected_from_event('store-a-fixture', incomplete), 'incomplete refund event rejected')
with TemporaryDirectory(prefix='bigcommerce-refund-local-') as directory:
    database = Path(directory) / 'candidates.sqlite'
    with sqlite3.connect(database) as connection:
        connection.execute('CREATE TABLE candidates (store_hash TEXT NOT NULL,refund_id INTEGER NOT NULL,order_id INTEGER NOT NULL,PRIMARY KEY(store_hash,refund_id))')
    first = record_candidate(database, verified)
    check(record_candidate(database, verified) == first, 'replayed candidate has same stored identity')
    with sqlite3.connect(database) as connection:
        check(connection.execute('SELECT count(*) FROM candidates').fetchone()[0] == 1, 'duplicate read creates no second candidate')
    # Deliberately contradictory fixture, not a claim that native refunds can change owner.
    rejects(lambda: record_candidate(database, wrong_order), 'contradiction cannot overwrite stored ownership')
    other_store = verify_read(ExpectedRefund('store-b-fixture', 900, 77), 'store-b-fixture', 200, response)
    record_candidate(database, other_store)
    with sqlite3.connect(database) as connection:
        check(connection.execute('SELECT count(*) FROM candidates').fetchone()[0] == 2, 'same numeric refund id in two stores stays separate')
        check(connection.execute('SELECT order_id FROM candidates WHERE store_hash=? AND refund_id=?', ('store-a-fixture', 77)).fetchone()[0] == 900, 'stored original owner survives contradiction')
    check(record_candidate(database, verified) == first, 'ownership persists after reopened connection')
print('CHECKS', checks)
print('BigCommerce requests: 0; payment actions: 0; ERP writes: 0.')
