// KL-182: BYOK setup modal. Fires once after a Pro/Venue license
// activation lands without an Anthropic key set. The Director tab's
// BYOK card is the always-available fallback — this modal is the
// "you just paid for this, here's the next step" prompt so users
// don't have to hunt for where the key goes.
//
// Trigger conditions (all must be true):
//   - active_path === 'needs_byok_key' from /api/director/status
//   - localStorage flag `conductor_byok_modal_dismissed` is not set
//
// active_path === 'needs_byok_key' is set by api_director.py when the
// tier is Pro/Venue, no key is present, and RELAY_UI_ENABLED is off.
// The modal will not fire for Starter (active_path === 'unavailable')
// nor for users who already have a key (active_path === 'byok').

const DISMISSED_KEY = 'conductor_byok_modal_dismissed';
const VALIDATE_DEBOUNCE_MS = 600;
const TIER_LABELS = {pro: 'Pro', venue: 'Venue'};

let validateTimer = null;

function storeDefinition() {
    return {
        shown: false,
        busy: false,
        tier: '',
        tierLabel: 'Pro',
        inputValue: '',
        statusKind: '',
        statusText: '',
        saveLabel: 'Save and continue',

        setStatus(kind, text) {
            this.statusKind = kind || '';
            this.statusText = text || '';
        },

        // Debounced validate-on-type — same shape as the Director tab's
        // BYOK card so the user sees instant feedback while pasting.
        onInput() {
            clearTimeout(validateTimer);
            const val = (this.inputValue || '').trim();
            if (!val) {
                this.setStatus('', '');
                return;
            }
            if (!val.startsWith('sk-ant-') || val.length < 30) {
                this.setStatus('pending', "Doesn't look complete yet…");
                return;
            }
            this.setStatus('checking', 'Checking…');
            validateTimer = setTimeout(() => this._validateOnly(val), VALIDATE_DEBOUNCE_MS);
        },

        // Validate without saving — gives quick feedback while user is
        // still typing/pasting. The actual save happens on button click.
        async _validateOnly(key) {
            try {
                const r = await fetch('/api/settings/api-key/test', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({key}),
                });
                const data = await r.json();
                if (!data.valid) {
                    this.setStatus('err', `Invalid: ${data.error || 'unknown'}`);
                } else {
                    this.setStatus('ok', '✓ Valid — click Save to continue');
                }
            } catch (e) {
                this.setStatus('err', `Network error: ${e.message || e}`);
            }
        },

        async saveAndClose() {
            if (this.busy) return;
            const key = (this.inputValue || '').trim();
            if (!key) return;
            this.busy = true;
            this.saveLabel = 'Saving…';
            try {
                // Validate then persist. The save endpoint flips
                // byok_enabled=true server-side as a side effect, so
                // the Director-status reflects the new key on next
                // fetch.
                const test = await fetch('/api/settings/api-key/test', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({key}),
                }).then(r => r.json());
                if (!test.valid) {
                    this.setStatus('err', `Invalid: ${test.error || 'unknown'}`);
                    return;
                }
                const save = await fetch('/api/settings/api-key', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({key}),
                });
                if (!save.ok) {
                    const err = await save.json().catch(() => ({}));
                    this.setStatus('err', `Save failed: ${err.error || 'unknown'}`);
                    return;
                }
                this.setStatus('ok', '✓ Saved');
                // Close after a brief beat so the success state is
                // visible. Don't set the dismissed flag — the no-key
                // condition itself is now false, so the modal won't
                // re-trigger anyway.
                setTimeout(() => { this.shown = false; }, 600);
            } catch (e) {
                this.setStatus('err', `Network error: ${e.message || e}`);
            } finally {
                this.busy = false;
                this.saveLabel = 'Save and continue';
            }
        },

        skip() {
            try { localStorage.setItem(DISMISSED_KEY, '1'); } catch (e) {
                // localStorage unavailable (private mode etc.) — modal
                // will re-trigger next launch, which is acceptable.
            }
            this.shown = false;
        },
    };
}

function ensureStore() {
    if (!window.Alpine) return null;
    if (!window.Alpine.store('byokSetup')) {
        window.Alpine.store('byokSetup', storeDefinition());
    }
    return window.Alpine.store('byokSetup');
}

if (window.Alpine) {
    ensureStore();
} else {
    document.addEventListener('alpine:init', ensureStore);
}

export async function initBYOKSetup() {
    // Cheap dismissed-flag check first — avoid even fetching status if
    // the user already opted out on this machine.
    let dismissed = false;
    try { dismissed = localStorage.getItem(DISMISSED_KEY) === '1'; } catch (e) { /* private mode */ }
    if (dismissed) return;

    let status;
    try {
        const r = await fetch('/api/director/status');
        if (!r.ok) return;
        status = await r.json();
    } catch (e) {
        // Status endpoint unreachable means we can't know if we should
        // fire. Fail silently — the Director tab fallback still works.
        return;
    }

    if (status.active_path !== 'needs_byok_key') return;

    const store = ensureStore();
    if (!store) return;
    store.tier = status.tier || 'pro';
    store.tierLabel = TIER_LABELS[store.tier] || 'Pro';
    store.shown = true;
}
