(() => {
    'use strict';

    const definitions = window.projectDatabaseDefinitions || {};
    const baseUrl = window.projectsBaseUrl || '';
    const modalElement = document.getElementById('databaseConnectionModal');
    const form = document.getElementById('databaseConnectionForm');
    if (!modalElement || !form) return;

    const modal = bootstrap.Modal.getOrCreateInstance(modalElement);
    const loading = document.getElementById('editDbLoading');
    const testButton = document.getElementById('editDbTestButton');
    const saveButton = document.getElementById('editDbSaveButton');
    const testResult = document.getElementById('editDbTestResult');
    const unsavedIndicator = document.getElementById('editDbUnsavedIndicator');
    const sourceWarning = document.getElementById('editDbSourceWarning');
    const sourceConfirmation = document.getElementById('editDbConfirmSourceChange');
    let activeSlug = '';
    let currentProfile = null;
    let loadedFingerprint = '';
    let testedFingerprint = '';

    function field(name) {
        return form.elements.namedItem(name);
    }

    function setVisible(id, visible) {
        document.getElementById(id)?.classList.toggle('d-none', !visible);
    }

    function setLoading(visible) {
        loading?.classList.toggle('is-hidden', !visible);
    }

    function setResult(state, message) {
        if (!testResult) return;
        testResult.className = 'database-test-result mt-3' + (state ? ` is-${state}` : '');
        testResult.replaceChildren();
        const icon = document.createElement('i');
        icon.className = 'bi ' + ({ success: 'bi-check-circle-fill', error: 'bi-exclamation-circle-fill', pending: 'bi-clock-history' }[state] || 'bi-info-circle');
        const text = document.createElement('span');
        text.textContent = message;
        testResult.append(icon, text);
    }

    function payload() {
        return Object.fromEntries(new FormData(form).entries());
    }

    function fingerprint() {
        const values = payload();
        delete values.confirm_source_change;
        return JSON.stringify(Object.keys(values).sort().reduce((result, key) => {
            result[key] = values[key];
            return result;
        }, {}));
    }

    function sourceKeyFromProfile(profile) {
        if (!profile) return '';
        if (profile.type === 'sqlite') return ['sqlite', profile.path || 'database/app.sqlite'].join('|');
        return [profile.type, profile.host || '', profile.port || '', profile.dbname || '', profile.schema || '', profile.service || ''].join('|');
    }

    function sourceKeyFromForm() {
        const type = String(field('db_type')?.value || 'mysql');
        if (type === 'sqlite') return ['sqlite', String(field('db_path')?.value || 'database/app.sqlite').trim()].join('|');
        return [
            type,
            String(field('db_host')?.value || '').trim(),
            String(field('db_port')?.value || '').trim(),
            String(field('db_name')?.value || '').trim(),
            String(field('db_schema')?.value || '').trim(),
            String(field('db_service')?.value || '').trim(),
        ].join('|');
    }

    function sourceChanged() {
        return currentProfile !== null && sourceKeyFromProfile(currentProfile) !== sourceKeyFromForm();
    }

    function connectionTarget(connection) {
        if (!connection) return '';
        if (connection.type === 'sqlite') {
            return String(connection.path || '').split(/[\\/]/).filter(Boolean).pop() || 'app.sqlite';
        }
        return String(connection.type === 'oci' ? (connection.service || '') : (connection.dbname || ''));
    }

    function updateProjectConnectionSummary(slug, data) {
        const targetValue = connectionTarget(data.connection);
        document.querySelectorAll('[data-project-slug]').forEach(container => {
            if (container.dataset.projectSlug !== slug) return;
            if (container.matches('[data-project-card]')) {
                container.dataset.database = data.db_type;
                container.dataset.databaseLabel = data.db_label;
                container.dataset.databaseTarget = targetValue;
                container.dataset.search = `${container.dataset.name || ''} ${slug} ${container.dataset.description || ''} ${data.db_label} ${container.dataset.health || ''}`.toLowerCase();
            }
            container.querySelectorAll('[data-project-db-label]').forEach(label => {
                label.textContent = data.db_label;
            });
            container.querySelectorAll('[data-project-db-target]').forEach(target => {
                target.textContent = targetValue;
            });
        });
    }

    function updateSourceWarning() {
        const changed = sourceChanged();
        sourceWarning?.classList.toggle('d-none', !changed);
        if (!changed && sourceConfirmation) sourceConfirmation.checked = false;
        return changed;
    }

    function updateActionState() {
        const currentFingerprint = fingerprint();
        const dirty = loadedFingerprint !== '' && currentFingerprint !== loadedFingerprint;
        const tested = testedFingerprint !== '' && currentFingerprint === testedFingerprint;
        const changed = updateSourceWarning();
        const confirmed = !changed || !!sourceConfirmation?.checked;

        if (unsavedIndicator) {
            unsavedIndicator.classList.toggle('is-dirty', dirty && !tested);
            unsavedIndicator.classList.toggle('is-tested', tested);
            unsavedIndicator.lastChild.textContent = tested
                ? ' Connection tested — ready to save'
                : (dirty ? ' Unsaved connection changes' : ' Saved profile loaded');
        }
        if (saveButton) saveButton.disabled = !tested || !confirmed;
    }

    function applyType(type, { resetDefaults = false, force = false } = {}) {
        const definition = definitions[type];
        if (!definition || (!definition.available && !force)) {
            if (definition) setResult('error', definition.requirement + ' before this database can be selected.');
            return false;
        }

        field('db_type').value = type;
        if (testButton) testButton.disabled = !definition.available;
        document.querySelectorAll('[data-edit-db-type]').forEach(choice => {
            const selected = choice.dataset.editDbType === type;
            choice.classList.toggle('active', selected);
            choice.setAttribute('aria-pressed', selected ? 'true' : 'false');
        });
        document.getElementById('editDbSelectedLabel').textContent = definition.label;
        document.getElementById('editDbSelectedDescription').textContent = definition.description;

        const sqlite = type === 'sqlite';
        // CockroachDB is Postgres wire-compatible; TiDB is MySQL wire-compatible —
        // so they share the exact same connection fields as their base engine.
        const postgres = type === 'pgsql' || type === 'cockroachdb';
        const mysqlFamily = type === 'mysql' || type === 'mariadb' || type === 'tidb';
        const sqlServer = type === 'sqlsrv';
        const oracle = type === 'oci';
        setVisible('editDbServerFields', !sqlite);
        setVisible('editDbSqliteFields', sqlite);
        setVisible('editDbCredentialFields', !sqlite);
        setVisible('editDbCharsetField', !sqlite);
        setVisible('editDbSchemaField', postgres || sqlServer);
        setVisible('editDbOracleServiceField', oracle);
        setVisible('editDbMysqlSslFields', mysqlFamily);
        setVisible('editDbPostgresSslField', postgres);
        setVisible('editDbSqlServerSecurityFields', sqlServer);

        field('db_host').required = !sqlite;
        field('db_port').required = !sqlite;
        field('db_name').required = !sqlite && !oracle;
        field('db_path').required = sqlite;
        field('db_service').required = oracle;
        document.getElementById('editDbNameLabel').textContent = oracle ? 'Database / SID (optional)' : 'Database name';

        if (resetDefaults) {
            if (!field('db_host').value) field('db_host').value = definition.default_host || '';
            field('db_port').value = definition.default_port || '';
            field('db_charset').value = definition.default_charset || '';
            if (sqlite && !field('db_path').value) field('db_path').value = 'database/app.sqlite';
            if (postgres) field('db_schema').value = 'public';
            if (sqlServer) field('db_schema').value = 'dbo';
            testedFingerprint = '';
            setResult('pending', 'Database type changed. Test the new connection before saving.');
        }
        updateActionState();
        return true;
    }

    function populate(profile, project) {
        currentProfile = profile;
        activeSlug = project.slug;
        document.getElementById('editDbProjectName').textContent = project.name;
        document.getElementById('editDbCurrentEngine').textContent = definitions[profile.type]?.label || profile.type;
        document.getElementById('editDbSavedSummary').textContent = profile.summary || '—';

        applyType(profile.type, { force: true });
        field('db_host').value = profile.host || '';
        field('db_port').value = profile.port || '';
        field('db_name').value = profile.dbname || '';
        field('db_path').value = profile.path || 'database/app.sqlite';
        field('db_user').value = profile.user || '';
        field('db_pass').disabled = false;
        field('db_pass').value = '';
        field('db_charset').value = profile.charset || '';
        field('db_schema').value = profile.schema || '';
        field('db_service').value = profile.service || '';
        field('db_ssl_mode').value = profile.ssl?.mode || 'prefer';
        field('db_ssl_ca').value = profile.ssl?.ca || '';
        document.getElementById('editDbSslEnabled').checked = !!profile.ssl?.enabled;
        document.getElementById('editDbEncrypt').checked = !!profile.ssl?.encrypt;
        document.getElementById('editDbTrustCertificate').checked = !!profile.ssl?.trust_server_certificate;
        document.getElementById('editDbClearPassword').checked = false;
        document.getElementById('editDbConfirmSourceChange').checked = false;
        document.getElementById('editDbPasswordHint').textContent = profile.password_saved
            ? 'A password is saved. Leave blank to keep it.'
            : 'No password is currently saved.';
        applyType(profile.type, { force: true });
        loadedFingerprint = fingerprint();
        testedFingerprint = '';
        if (testButton) testButton.disabled = !definitions[profile.type]?.available;
        setResult(
            definitions[profile.type]?.available ? '' : 'error',
            definitions[profile.type]?.available
                ? 'Test the connection after making changes. Saving remains locked until the current values pass.'
                : (definitions[profile.type]?.requirement || 'The required PDO driver is not available on this server.')
        );
        updateActionState();
    }

    async function readJson(response) {
        const text = await response.text();
        try { return JSON.parse(text); }
        catch { throw new Error('The server returned an invalid response.'); }
    }

    async function openEditor(slug, projectName) {
        activeSlug = slug;
        currentProfile = null;
        loadedFingerprint = '';
        testedFingerprint = '';
        form.reset();
        if (testButton) testButton.disabled = true;
        if (saveButton) saveButton.disabled = true;
        document.getElementById('editDbProjectName').textContent = projectName || 'project';
        document.getElementById('editDbCurrentEngine').textContent = 'Loading…';
        setLoading(true);
        modal.show();
        try {
            const response = await fetch(`${baseUrl}/project/${encodeURIComponent(slug)}/database-connection`, { headers: { Accept: 'application/json' } });
            const data = await readJson(response);
            if (!response.ok || !data.success) throw new Error(data.message || 'Could not load the connection profile.');
            populate(data.connection, data.project);
        } catch (error) {
            setResult('error', error.message || 'Could not load the connection profile.');
            if (testButton) testButton.disabled = true;
        } finally {
            setLoading(false);
        }
    }

    document.querySelectorAll('[data-edit-database]').forEach(button => {
        button.addEventListener('click', event => {
            event.preventDefault();
            event.stopPropagation();
            const settingsModalElement = button.closest('#projectSettingsModal');
            const launch = () => openEditor(button.dataset.projectSlug || '', button.dataset.projectName || '');
            if (settingsModalElement?.classList.contains('show')) {
                bootstrap.Modal.getInstance(settingsModalElement)?.hide();
                settingsModalElement.addEventListener('hidden.bs.modal', launch, { once: true });
            } else {
                launch();
            }
        });
    });

    document.querySelectorAll('[data-edit-db-type]').forEach(choice => {
        choice.addEventListener('click', () => applyType(choice.dataset.editDbType, { resetDefaults: true }));
    });

    form.addEventListener('input', event => {
        if (event.target.id === 'editDbConfirmSourceChange') {
            updateActionState();
            return;
        }
        if (testedFingerprint && testedFingerprint !== fingerprint()) {
            testedFingerprint = '';
            setResult('pending', 'Connection values changed. Test again before saving.');
        }
        updateActionState();
    });

    document.getElementById('editDbClearPassword')?.addEventListener('change', event => {
        const password = field('db_pass');
        password.disabled = event.target.checked;
        if (event.target.checked) password.value = '';
        updateActionState();
    });

    document.getElementById('editDbTogglePassword')?.addEventListener('click', function () {
        const password = document.getElementById('editDbPassword');
        const reveal = password.type === 'password';
        password.type = reveal ? 'text' : 'password';
        this.querySelector('i').className = 'bi ' + (reveal ? 'bi-eye-slash' : 'bi-eye');
    });

    testButton?.addEventListener('click', async () => {
        if (!form.reportValidity() || !activeSlug) return;
        const original = testButton.innerHTML;
        testButton.disabled = true;
        testButton.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Testing…';
        setResult('pending', 'Connecting to the selected database…');
        try {
            const response = await fetch(`${baseUrl}/project/${encodeURIComponent(activeSlug)}/database-connection/test`, {
                method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify(payload()),
            });
            const data = await readJson(response);
            if (!response.ok || !data.success) throw new Error(data.message || 'Connection test failed.');
            testedFingerprint = fingerprint();
            setResult('success', data.message || 'Connection successful.');
        } catch (error) {
            testedFingerprint = '';
            setResult('error', error.message || 'Connection test failed.');
        } finally {
            testButton.disabled = false;
            testButton.innerHTML = original;
            updateActionState();
        }
    });

    form.addEventListener('submit', async event => {
        event.preventDefault();
        if (!activeSlug || testedFingerprint !== fingerprint()) {
            setResult('pending', 'Test the current connection values before saving.');
            return;
        }
        if (sourceChanged() && !sourceConfirmation?.checked) {
            sourceConfirmation?.focus();
            setResult('pending', 'Confirm the data-source switch before saving.');
            updateActionState();
            return;
        }

        const original = saveButton.innerHTML;
        saveButton.disabled = true;
        saveButton.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Saving…';
        try {
            const response = await fetch(`${baseUrl}/project/${encodeURIComponent(activeSlug)}/database-connection`, {
                method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify(payload()),
            });
            const data = await readJson(response);
            if (!response.ok || !data.success) throw new Error(data.message || 'Connection could not be saved.');

            updateProjectConnectionSummary(activeSlug, data);
            modal.hide();

            const alert = data.needs_sync
                ? await Swal.fire({
                    title: 'Connection saved',
                    text: 'The project now uses the new data source. Open Tables and synchronize the database before continuing design work.',
                    icon: 'success',
                    showCancelButton: true,
                    confirmButtonText: 'Open Tables',
                    cancelButtonText: 'Stay on Projects',
                })
                : await Swal.fire('Connection saved', data.message, 'success');
            if (data.needs_sync && alert.isConfirmed) {
                window.location.href = `${baseUrl}/project/${encodeURIComponent(activeSlug)}/tables`;
            }
        } catch (error) {
            setResult('error', error.message || 'Connection could not be saved.');
        } finally {
            saveButton.disabled = false;
            saveButton.innerHTML = original;
            updateActionState();
        }
    });

    modalElement.addEventListener('hidden.bs.modal', () => {
        activeSlug = '';
        currentProfile = null;
        loadedFingerprint = '';
        testedFingerprint = '';
        form.reset();
    });
})();
