"""Local connection revisions are scoped to a Manager and server, not DB row IDs."""
import hashlib
import json

import field_profiles as profiles
from field_ui import print, input

MAX_VERSION = 9_007_199_254_740_991


def valid(value):
    if type(value) is not int or not 1 <= value <= MAX_VERSION:
        raise ValueError('connection_version must be an integer in 1..9007199254740991')
    return value


def context(manager, server_id):
    manager = profiles.url(manager, origin=True)
    if not isinstance(server_id, str) or not server_id.strip():
        raise ValueError('AI server ID required')
    return {'manager': manager, 'server_id': server_id}


def location(identity):
    key = hashlib.sha256(json.dumps(identity, sort_keys=True).encode()).hexdigest()
    return profiles.STATE / 'connections' / (key + '.json')


def read(identity):
    path = location(identity)
    value = profiles.read_json(path) if path.exists() else {'identity': identity}
    if value.get('identity') != identity:
        raise ValueError('Connection history identity mismatch')
    return value


def fingerprint(body):
    # Login passwords and CA rotation are not part of Web's connection revision.
    value = {k: body.get(k) for k in ('advertisedIp', 'controlPort', 'bboxStreamKey')}
    redis = body.get('redis', {})
    value['redis'] = {k: redis.get(k) for k in ('host', 'port', 'db', 'username', 'password', 'tls')}
    return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':')).encode()).hexdigest()


def choose(identity, body, override=None, baseline=None):
    if override is not None:
        return valid(override)
    history = read(identity)
    candidates = [history[k] for k in ('prepared', 'successful') if history.get(k)]
    if baseline is not None:
        candidates.append({'version': valid(baseline['connectionConfigVersion']),
                           'fingerprint': fingerprint(baseline)})
    if not candidates:
        return 1  # Only a local starting value, never a claim about the Web DB.
    latest = max(candidates, key=lambda x: valid(x['version']))
    if latest['fingerprint'] == fingerprint(body):
        return latest['version']
    return valid(latest['version'] + 1)


def remember(identity, stage, body, kind):
    if kind not in ('prepared', 'successful'):
        raise ValueError('Invalid connection history phase')
    value = read(identity)
    value[kind] = {'version': valid(body['connectionConfigVersion']),
                   'fingerprint': fingerprint(body), 'stage': str(stage)}
    profiles.atomic(location(identity), value)


def show(profile):
    web = profiles.web(profile)
    identity = context(web['manager'], web['server_id'])
    value = read(identity)
    print('Web:', identity['manager'], 'AI ID:', identity['server_id'])
    print('connection_version override:', web.get('connection_version', 'auto'))
    for key, label in (('prepared', 'Local prepared version:'), ('successful', 'Local successful version:')):
        print(label, value.get(key, {}).get('version', 'unknown'))
    print('Web current version: UNKNOWN (no remote query or login).')
    print('Profile version=1 is its file format. It is NOT connection_version.')


def configure(args):
    p = profiles.load()
    if args == ['status']:
        show(p)
        print('Set: ct config version NUMBER | auto (no login)')
        return
    if not args:
        show(p)
        value=input('Connection version NUMBER / auto [Enter=keep current]: ').strip()
        if not value:return
        args=[value]
    if len(args) != 1:
        raise ValueError('ct config version NUMBER | auto | status')
    if (profiles.STATE / 'pending.json').exists():
        raise ValueError('Preserve/cancel the pending request first: ct config cancel; then set version and prepare.')
    raw = args[0]
    if raw != 'auto' and (not raw.isascii() or not raw.isdecimal()):
        raise ValueError('Use a positive integer or auto')
    version = None if raw == 'auto' else valid(int(raw))
    if version is None:
        profiles.web(p).pop('connection_version', None)
    else:
        profiles.web(p)['connection_version'] = version
    profiles.save(p)
    show(profiles.load(p['id']))
    print('Saved locally. No remote request. Next: ct config prepare')
