<!--
Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}

{% block title %}Providers - AISBF Dashboard{% endblock %}

{% block content %}
<h2 style="margin-bottom: 30px;">Providers Configuration</h2>

{% if success %}
<div class="alert alert-success">{{ success }}</div>
{% endif %}

{% if error %}
<div class="alert alert-error">{{ error }}</div>
{% endif %}

<!-- New Provider Form (hidden by default) -->
<div id="new-provider-form" style="display: none; background: #16213e; border: 2px solid #4a9eff; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
    <h3 style="margin: 0 0 20px 0; color: #4a9eff;">Add New Provider</h3>
    
    <div class="form-group">
        <label>Provider Key (unique identifier, e.g., "gemini", "openai", "kiro")</label>
        <input type="text" id="new-provider-key" placeholder="Enter provider key" style="width: 100%;">
        <small style="color: #a0a0a0; display: block; margin-top: 5px;">This will be used as the provider ID in the configuration and API endpoints</small>
    </div>
    
    <div class="form-group">
        <label>Provider Type</label>
        <select id="new-provider-type" onchange="updateNewProviderDefaults()">
            <option value="openai">OpenAI</option>
            <option value="google">Google</option>
            <option value="anthropic">Anthropic</option>
            <option value="ollama">Ollama</option>
            <option value="kiro">Kiro (Amazon Q Developer)</option>
            <option value="claude">Claude (OAuth2)</option>
            <option value="kilocode">Kilocode (OAuth2)</option>
        </select>
        <small style="color: #a0a0a0; display: block; margin-top: 5px;">Select the type of provider to configure appropriate settings</small>
    </div>
    
    <div id="new-provider-type-info" style="background: #0f2840; padding: 15px; border-radius: 5px; margin-bottom: 15px; border-left: 3px solid #4a9eff;">
        <p style="margin: 0; color: #a0a0a0;" id="new-provider-type-description">
            OpenAI-compatible provider. Uses standard API key authentication.
        </p>
    </div>
    
    <div style="display: flex; gap: 10px;">
        <button type="button" class="btn" onclick="confirmAddProvider()">Create Provider</button>
        <button type="button" class="btn btn-secondary" onclick="cancelAddProvider()">Cancel</button>
    </div>
</div>

<div id="providers-list" style="margin-bottom: 20px;">
    <!-- Provider list will be rendered here -->
</div>

<button type="button" class="btn" id="add-provider-btn" onclick="showAddProviderForm()" style="margin-top: 20px;">Add Provider</button>
<div style="display: flex; gap: 10px; margin-top: 20px;">
    <button type="button" class="btn" onclick="saveProviders()">Save Configuration</button>
    <a href="/dashboard" class="btn btn-secondary">Cancel</a>
</div>

<script>
let providersData = {{ providers_json | safe }};
let expandedProviders = new Set();

function renderProvidersList() {
    const container = document.getElementById('providers-list');
    container.innerHTML = '';
    
    Object.entries(providersData).forEach(([key, provider]) => {
        const providerItem = document.createElement('div');
        providerItem.className = 'provider-item';
        providerItem.style.cssText = 'border: 1px solid #0f3460; margin-bottom: 10px; border-radius: 5px; background: #1a1a2e;';
        
        const isExpanded = expandedProviders.has(key);
        
        providerItem.innerHTML = `
            <div class="provider-header" onclick="toggleProvider('${key}')" style="padding: 15px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; user-select: none;">
                <div style="display: flex; align-items: center; gap: 10px;">
                    <span style="font-size: 18px;">${isExpanded ? '▼' : '▶'}</span>
                    <strong style="font-size: 16px;">${key}</strong>
                    <span style="color: #a0a0a0; font-size: 14px;">(${provider.name || key})</span>
                </div>
                <button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); removeProvider('${key}')" style="background: #dc3545; padding: 5px 15px;">Remove</button>
            </div>
            <div id="provider-details-${key}" style="display: ${isExpanded ? 'block' : 'none'}; padding: 20px; border-top: 1px solid #0f3460; background: #16213e;">
                <!-- Details will be rendered here -->
            </div>
        `;
        
        container.appendChild(providerItem);
        
        if (isExpanded) {
            renderProviderDetails(key);
        }
    });
}

function toggleProvider(key) {
    if (expandedProviders.has(key)) {
        expandedProviders.delete(key);
    } else {
        expandedProviders.add(key);
    }
    renderProvidersList();
}

function renderProviderDetails(key) {
    const container = document.getElementById(`provider-details-${key}`);
    const provider = providersData[key];
    const isKiroProvider = provider.type === 'kiro';
    const isClaudeProvider = provider.type === 'claude';
    const isKiloProvider = provider.type === 'kilocode';
    
    // Initialize kiro_config if this is a kiro provider and doesn't have it
    if (isKiroProvider && !provider.kiro_config) {
        provider.kiro_config = {
            region: 'us-east-1',
            creds_file: '',
            sqlite_db: '',
            refresh_token: '',
            profile_arn: '',
            client_id: '',
            client_secret: ''
        };
    }
    
    // Initialize claude_config if this is a claude provider and doesn't have it
    if (isClaudeProvider && !provider.claude_config) {
        provider.claude_config = {
            credentials_file: '~/.claude_credentials.json'
        };
    }
    
    // Initialize kilo_config if this is a kilocode provider and doesn't have it
    if (isKiloProvider && !provider.kilo_config) {
        provider.kilo_config = {
            credentials_file: '~/.kilo_credentials.json',
            api_base: 'https://api.kilo.ai/api/gateway'
        };
    }
    
    const kiroConfig = provider.kiro_config || {};
    const claudeConfig = provider.claude_config || {};
    const kiloConfig = provider.kilo_config || {};
    
    // Build authentication fields based on provider type
    let authFieldsHtml = '';
    if (isKiroProvider) {
        // Kiro-specific authentication fields
        authFieldsHtml = `
            <div style="background: #0f2840; padding: 15px; border-radius: 5px; margin-bottom: 15px; border-left: 3px solid #4a9eff;">
                <h4 style="margin: 0 0 15px 0; color: #4a9eff;">Kiro Authentication</h4>
                <small style="color: #a0a0a0; display: block; margin-bottom: 15px;">
                    Choose one authentication method: Kiro IDE credentials (creds_file), kiro-cli database (sqlite_db), or direct credentials (refresh_token + client_id/secret).
                </small>
                
                <div class="form-group">
                    <label>AWS Region</label>
                    <input type="text" value="${kiroConfig.region || 'us-east-1'}" onchange="updateKiroConfig('${key}', 'region', this.value)" placeholder="us-east-1">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">AWS region for Kiro API (default: us-east-1)</small>
                </div>
                
                <h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Option 1: Kiro IDE Credentials</h5>
                <div class="form-group">
                    <label>Credentials File Path</label>
                    <input type="text" value="${kiroConfig.creds_file || ''}" onchange="updateKiroConfig('${key}', 'creds_file', this.value)" placeholder="~/.config/Code/User/globalStorage/amazon.q/credentials.json">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Path to Kiro IDE credentials JSON file</small>
                </div>
                
                <h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Option 2: kiro-cli Database</h5>
                <div class="form-group">
                    <label>SQLite Database Path</label>
                    <input type="text" value="${kiroConfig.sqlite_db || ''}" onchange="updateKiroConfig('${key}', 'sqlite_db', this.value)" placeholder="~/.local/share/kiro-cli/data.sqlite3">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Path to kiro-cli SQLite database</small>
                </div>
                
                <h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Option 3: Direct Credentials</h5>
                <div class="form-group">
                    <label>Refresh Token</label>
                    <input type="password" value="${kiroConfig.refresh_token || ''}" onchange="updateKiroConfig('${key}', 'refresh_token', this.value)" placeholder="Enter refresh token">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Kiro refresh token for direct authentication</small>
                </div>
                
                <div class="form-group">
                    <label>Profile ARN</label>
                    <input type="text" value="${kiroConfig.profile_arn || ''}" onchange="updateKiroConfig('${key}', 'profile_arn', this.value)" placeholder="arn:aws:codewhisperer:us-east-1:...">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">AWS CodeWhisperer profile ARN (optional)</small>
                </div>
                
                <div class="form-group">
                    <label>Client ID (for AWS SSO OIDC)</label>
                    <input type="text" value="${kiroConfig.client_id || ''}" onchange="updateKiroConfig('${key}', 'client_id', this.value)" placeholder="OAuth client ID">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">OAuth client ID for AWS SSO OIDC authentication</small>
                </div>
                
                <div class="form-group">
                    <label>Client Secret (for AWS SSO OIDC)</label>
                    <input type="password" value="${kiroConfig.client_secret || ''}" onchange="updateKiroConfig('${key}', 'client_secret', this.value)" placeholder="OAuth client secret">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">OAuth client secret for AWS SSO OIDC authentication</small>
                </div>
                
                <h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Option 4: Upload Files</h5>
                <div class="form-group">
                    <label>Upload Credentials File</label>
                    <input type="file" id="kiro-creds-file-${key}" accept=".json" onchange="uploadKiroFile('${key}', 'creds_file', this.files[0])">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Upload Kiro IDE credentials JSON file</small>
                </div>
                <div class="form-group">
                    <label>Upload SQLite Database</label>
                    <input type="file" id="kiro-db-file-${key}" accept=".sqlite3,.db,.sqlite" onchange="uploadKiroFile('${key}', 'sqlite_db', this.files[0])">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Upload kiro-cli SQLite database file</small>
                </div>
                <div id="kiro-upload-status-${key}" style="margin-top: 10px;"></div>
            </div>
        `;
    } else if (isKiloProvider) {
        // Kilocode authentication fields - supports both API key and OAuth2
        authFieldsHtml = `
            <div style="background: #0f2840; padding: 15px; border-radius: 5px; margin-bottom: 15px; border-left: 3px solid #4a9eff;">
                <h4 style="margin: 0 0 15px 0; color: #4a9eff;">Kilocode Authentication</h4>
                <small style="color: #a0a0a0; display: block; margin-bottom: 15px;">
                    Choose your authentication method: API Key (recommended for simplicity) or OAuth2 Device Authorization Grant.
                </small>
                
                <h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Option 1: API Key / Auth Token</h5>
                <div class="form-group">
                    <label>API Key</label>
                    <input type="password" value="${provider.api_key || ''}" onchange="updateProvider('${key}', 'api_key', this.value)" placeholder="Enter your Kilocode API key">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">If provided, API key authentication will be used instead of OAuth2</small>
                </div>
                
                <h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Option 2: OAuth2 Authentication</h5>
                <small style="color: #a0a0a0; display: block; margin-bottom: 15px;">
                    If no API key is provided, OAuth2 will be used automatically.
                </small>
                
                <div class="form-group">
                    <label>API Base URL</label>
                    <input type="text" value="${kiloConfig.api_base || 'https://api.kilo.ai/api/gateway'}" readonly style="background: #0f2840; cursor: not-allowed;" placeholder="https://api.kilo.ai/api/gateway">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Kilocode API base URL (fixed)</small>
                </div>
                
                <div class="form-group">
                    <label>Credentials File Path</label>
                    <input type="text" value="${kiloConfig.credentials_file || '~/.kilo_credentials.json'}" onchange="updateKiloConfig('${key}', 'credentials_file', this.value)" placeholder="~/.kilo_credentials.json">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Path where OAuth2 credentials will be stored</small>
                </div>
                
                <div style="margin-top: 15px;">
                    <button type="button" class="btn" onclick="authenticateKilo('${key}')" style="background: #4a9eff;">
                        🔐 Authenticate with Kilocode OAuth2
                    </button>
                    <button type="button" class="btn btn-secondary" onclick="checkKiloAuth('${key}')" style="margin-left: 10px;">
                        Check Status
                    </button>
                </div>
                
                <div id="kilo-auth-status-${key}" style="margin-top: 10px; padding: 10px; border-radius: 3px; display: none;">
                    <!-- Auth status will be displayed here -->
                </div>
                
                <h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Or Upload OAuth2 Credentials File</h5>
                <div class="form-group">
                    <label>Upload Credentials File</label>
                    <input type="file" id="kilo-creds-file-${key}" accept=".json" onchange="uploadKiloFile('${key}', this.files[0])">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Upload Kilocode OAuth2 credentials JSON file</small>
                </div>
                <div id="kilo-upload-status-${key}" style="margin-top: 10px;"></div>
            </div>
        `;
    } else if (isClaudeProvider) {
        // Claude OAuth2 authentication fields
        authFieldsHtml = `
            <div style="background: #0f2840; padding: 15px; border-radius: 5px; margin-bottom: 15px; border-left: 3px solid #4a9eff;">
                <h4 style="margin: 0 0 15px 0; color: #4a9eff;">Claude OAuth2 Authentication</h4>
                <small style="color: #a0a0a0; display: block; margin-bottom: 15px;">
                    Claude uses OAuth2 authentication. Click "Authenticate" to start the OAuth2 flow in your browser.
                </small>
                
                <div class="form-group">
                    <label>Credentials File Path</label>
                    <input type="text" value="${claudeConfig.credentials_file || '~/.claude_credentials.json'}" onchange="updateClaudeConfig('${key}', 'credentials_file', this.value)" placeholder="~/.claude_credentials.json">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Path where OAuth2 credentials will be stored</small>
                </div>
                
                <div style="margin-top: 15px;">
                    <button type="button" class="btn" onclick="authenticateClaude('${key}')" style="background: #4a9eff;">
                        🔐 Authenticate with Claude
                    </button>
                    <button type="button" class="btn btn-secondary" onclick="checkClaudeAuth('${key}')" style="margin-left: 10px;">
                        Check Status
                    </button>
                </div>
                
                <div id="claude-auth-status-${key}" style="margin-top: 10px; padding: 10px; border-radius: 3px; display: none;">
                    <!-- Auth status will be displayed here -->
                </div>
                
                <h5 style="margin: 20px 0 10px 0; color: #8ec8ff;">Or Upload Credentials File</h5>
                <div class="form-group">
                    <label>Upload Credentials File</label>
                    <input type="file" id="claude-creds-file-${key}" accept=".json" onchange="uploadClaudeFile('${key}', this.files[0])">
                    <small style="color: #a0a0a0; display: block; margin-top: 5px;">Upload Claude OAuth2 credentials JSON file</small>
                </div>
                <div id="claude-upload-status-${key}" style="margin-top: 10px;"></div>
            </div>
        `;
    } else {
        // Standard API key authentication fields
        authFieldsHtml = `
            <div class="form-group">
                <label>
                    <input type="checkbox" ${provider.api_key_required ? 'checked' : ''} onchange="updateProvider('${key}', 'api_key_required', this.checked)">
                    API Key Required
                </label>
            </div>
            
            <div class="form-group">
                <label>API Key</label>
                <input type="password" value="${provider.api_key || ''}" onchange="updateProvider('${key}', 'api_key', this.value)" placeholder="Enter API key">
            </div>
        `;
    }
    
    container.innerHTML = `
        <div class="form-group">
            <label>Provider ID</label>
            <input type="text" value="${provider.id}" onchange="updateProvider('${key}', 'id', this.value)" required>
        </div>
        
        <div class="form-group">
            <label>Name</label>
            <input type="text" value="${provider.name}" onchange="updateProvider('${key}', 'name', this.value)" required>
        </div>
        
        <div class="form-group">
            <label>Endpoint</label>
            <input type="text" value="${provider.endpoint}" onchange="updateProvider('${key}', 'endpoint', this.value)" ${provider.type === 'kilocode' ? 'readonly style="background: #0f2840; cursor: not-allowed;"' : ''} required>
            ${isKiroProvider ? '<small style="color: #a0a0a0; display: block; margin-top: 5px;">Typically: https://q.us-east-1.amazonaws.com</small>' : ''}
            ${provider.type === 'kilocode' ? '<small style="color: #a0a0a0; display: block; margin-top: 5px;">Fixed endpoint for Kilocode provider</small>' : ''}
        </div>
        
        <div class="form-group">
            <label>Type</label>
            <select onchange="updateProviderType('${key}', this.value)" required>
                <option value="google" ${provider.type === 'google' ? 'selected' : ''}>Google</option>
                <option value="openai" ${provider.type === 'openai' ? 'selected' : ''}>OpenAI</option>
                <option value="anthropic" ${provider.type === 'anthropic' ? 'selected' : ''}>Anthropic</option>
                <option value="ollama" ${provider.type === 'ollama' ? 'selected' : ''}>Ollama</option>
                <option value="kiro" ${provider.type === 'kiro' ? 'selected' : ''}>Kiro (Amazon Q Developer)</option>
                <option value="claude" ${provider.type === 'claude' ? 'selected' : ''}>Claude (OAuth2)</option>
                <option value="kilocode" ${provider.type === 'kilocode' ? 'selected' : ''}>Kilocode (OAuth2)</option>
            </select>
        </div>
        
        ${authFieldsHtml}
        
        <div class="form-group">
            <label>Rate Limit (seconds)</label>
            <input type="number" value="${provider.rate_limit || 0}" onchange="updateProvider('${key}', 'rate_limit', parseFloat(this.value))" step="0.1">
            <small style="color: #a0a0a0; display: block; margin-top: 5px;">Time delay between requests to this provider</small>
        </div>
        
        <div class="form-group">
            <label>Default Rate Limit TPM (Tokens Per Minute)</label>
            <input type="number" value="${provider.default_rate_limit_TPM || ''}" onchange="updateProvider('${key}', 'default_rate_limit_TPM', this.value ? parseInt(this.value) : null)" placeholder="Optional">
            <small style="color: #a0a0a0; display: block; margin-top: 5px;">Default token limit per minute for models in this provider</small>
        </div>
        
        <div class="form-group">
            <label>Default Rate Limit TPH (Tokens Per Hour)</label>
            <input type="number" value="${provider.default_rate_limit_TPH || ''}" onchange="updateProvider('${key}', 'default_rate_limit_TPH', this.value ? parseInt(this.value) : null)" placeholder="Optional">
            <small style="color: #a0a0a0; display: block; margin-top: 5px;">Default token limit per hour for models in this provider</small>
        </div>
        
        <div class="form-group">
            <label>Default Rate Limit TPD (Tokens Per Day)</label>
            <input type="number" value="${provider.default_rate_limit_TPD || ''}" onchange="updateProvider('${key}', 'default_rate_limit_TPD', this.value ? parseInt(this.value) : null)" placeholder="Optional">
            <small style="color: #a0a0a0; display: block; margin-top: 5px;">Default token limit per day for models in this provider</small>
        </div>
        
        <div class="form-group">
            <label>Default Max Request Tokens</label>
            <input type="number" value="${provider.default_max_request_tokens || ''}" onchange="updateProvider('${key}', 'default_max_request_tokens', this.value ? parseInt(this.value) : null)" placeholder="Optional">
        </div>
        
        <div class="form-group">
            <label>Default Context Size</label>
            <input type="number" value="${provider.default_context_size || ''}" onchange="updateProvider('${key}', 'default_context_size', this.value ? parseInt(this.value) : null)" placeholder="Optional">
        </div>
        
        <div class="form-group">
            <label>Default Condense Context (%)</label>
            <input type="number" value="${provider.default_condense_context || ''}" onchange="updateProvider('${key}', 'default_condense_context', this.value ? parseInt(this.value) : null)" placeholder="Optional (e.g., 80)">
        </div>
        
        <div class="form-group">
            <label>Default Condense Method (conversational, semantic, hierarchical, algorithmic)</label>
            <input type="text" value="${Array.isArray(provider.default_condense_method) ? provider.default_condense_method.join(', ') : (provider.default_condense_method || '')}" onchange="updateProviderCondenseMethod('${key}', this.value)" placeholder="e.g., semantic, conversational">
        </div>
        
        <div class="form-group">
            <label>
                <input type="checkbox" ${provider.nsfw ? 'checked' : ''} onchange="updateProvider('${key}', 'nsfw', this.checked)">
                NSFW
            </label>
        </div>
        
        <div class="form-group">
            <label>
                <input type="checkbox" ${provider.privacy ? 'checked' : ''} onchange="updateProvider('${key}', 'privacy', this.checked)">
                Privacy
            </label>
        </div>

        <h4 style="margin-top: 20px; margin-bottom: 10px;">Native Caching</h4>
        <small style="color: #a0a0a0; display: block; margin-bottom: 15px;">
            Provider-native caching features (Anthropic cache_control, Google Context Caching) for cost reduction.
        </small>

        <div class="form-group">
            <label>
                <input type="checkbox" ${provider.enable_native_caching ? 'checked' : ''} onchange="updateProvider('${key}', 'enable_native_caching', this.checked)">
                Enable Native Caching
            </label>
            <small style="color: #a0a0a0; display: block; margin-top: 5px;">Enable provider-native caching for cost reduction (50-70% savings for supported providers)</small>
        </div>

        <div class="form-group">
            <label>Cache TTL (seconds)</label>
            <input type="number" value="${provider.cache_ttl || ''}" onchange="updateProvider('${key}', 'cache_ttl', this.value ? parseInt(this.value) : null)" placeholder="Optional (e.g., 3600)">
            <small style="color: #a0a0a0; display: block; margin-top: 5px;">Cache time-to-live in seconds (Google Context Caching only)</small>
        </div>

        <div class="form-group">
            <label>Min Cacheable Tokens</label>
            <input type="number" value="${provider.min_cacheable_tokens || 1000}" onchange="updateProvider('${key}', 'min_cacheable_tokens', this.value ? parseInt(this.value) : 1000)" placeholder="1000">
            <small style="color: #a0a0a0; display: block; margin-top: 5px;">Minimum token count for content to be cacheable (default: 1000)</small>
        </div>

        <h4 style="margin-top: 20px; margin-bottom: 10px;">Models</h4>
        <small style="color: #a0a0a0; display: block; margin-bottom: 15px;">
            Configure specific models for this provider, or leave empty to automatically fetch all available models from the provider's API.
        </small>

        <div class="form-group" style="background: #0f2840; padding: 15px; border-radius: 5px; margin-bottom: 15px; border-left: 3px solid #4a9eff;">
            <label>Model Filter (for auto-fetched models)</label>
            <input type="text" value="${provider.model_filter || ''}" onchange="updateProvider('${key}', 'model_filter', this.value)" placeholder="e.g., free, pro, flash">
            <small style="color: #a0a0a0; display: block; margin-top: 5px;">
                When no models are manually configured, only expose models whose ID contains this filter word (case-insensitive wildcard matching).
                <br>Example: "free" will match models like "model-name-free", "free-model", "gpt-4-free-tier", etc.
            </small>
        </div>

        <div id="models-${key}"></div>
        <div style="display: flex; gap: 10px; margin-top: 10px;">
            <button type="button" class="btn" onclick="getModelsFromProvider('${key}')" style="background: #4a9eff;">
                🔄 Get Models from Provider
            </button>
            <button type="button" class="btn btn-secondary" onclick="addModel('${key}')">Add Model Manually</button>
        </div>
        <div id="get-models-status-${key}" style="margin-top: 10px;"></div>
    `;
    
    renderModels(key);
}

function renderModels(providerKey) {
    const container = document.getElementById(`models-${providerKey}`);
    const provider = providersData[providerKey];
    
    if (!provider.models || provider.models.length === 0) {
        container.innerHTML = '<p style="color: #a0a0a0;">No models configured</p>';
        return;
    }
    
    container.innerHTML = '';
    provider.models.forEach((model, index) => {
        const modelDiv = document.createElement('div');
        modelDiv.style.cssText = 'border: 1px solid #0f3460; padding: 15px; margin-bottom: 10px; border-radius: 3px; background: #1a1a2e;';
        
        modelDiv.innerHTML = `
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
                <strong>Model ${index + 1}</strong>
                <button type="button" class="btn btn-secondary" onclick="removeModel('${providerKey}', ${index})" style="background: #dc3545; padding: 5px 10px; font-size: 12px;">Remove</button>
            </div>
            
            <div class="form-group">
                <label>Model Name</label>
                <input type="text" value="${model.name}" onchange="updateModel('${providerKey}', ${index}, 'name', this.value)" required>
            </div>
            
            <div class="form-group">
                <label>Rate Limit (seconds)</label>
                <input type="number" value="${model.rate_limit || 0}" onchange="updateModel('${providerKey}', ${index}, 'rate_limit', parseFloat(this.value))" step="0.1">
            </div>
            
            <div class="form-group">
                <label>Max Request Tokens</label>
                <input type="number" value="${model.max_request_tokens || ''}" onchange="updateModel('${providerKey}', ${index}, 'max_request_tokens', this.value ? parseInt(this.value) : null)" placeholder="Optional">
            </div>
            
            <div class="form-group">
                <label>Context Size</label>
                <input type="number" value="${model.context_size || ''}" onchange="updateModel('${providerKey}', ${index}, 'context_size', this.value ? parseInt(this.value) : null)" placeholder="Optional">
            </div>
            
            <div class="form-group">
                <label>Rate Limit TPM (Tokens Per Minute)</label>
                <input type="number" value="${model.rate_limit_TPM || ''}" onchange="updateModel('${providerKey}', ${index}, 'rate_limit_TPM', this.value ? parseInt(this.value) : null)" placeholder="Optional">
            </div>
            
            <div class="form-group">
                <label>Rate Limit TPH (Tokens Per Hour)</label>
                <input type="number" value="${model.rate_limit_TPH || ''}" onchange="updateModel('${providerKey}', ${index}, 'rate_limit_TPH', this.value ? parseInt(this.value) : null)" placeholder="Optional">
            </div>
            
            <div class="form-group">
                <label>Rate Limit TPD (Tokens Per Day)</label>
                <input type="number" value="${model.rate_limit_TPD || ''}" onchange="updateModel('${providerKey}', ${index}, 'rate_limit_TPD', this.value ? parseInt(this.value) : null)" placeholder="Optional">
            </div>
            
            <div class="form-group">
                <label>Condense Context (%)</label>
                <input type="number" value="${model.condense_context || ''}" onchange="updateModel('${providerKey}', ${index}, 'condense_context', this.value ? parseInt(this.value) : null)" placeholder="Optional (default: 80)">
            </div>
            
            <div class="form-group">
                <label>Condense Method (conversational, semantic, hierarchical, algorithmic)</label>
                <input type="text" value="${Array.isArray(model.condense_method) ? model.condense_method.join(', ') : (model.condense_method || '')}" onchange="updateModelCondenseMethod('${providerKey}', ${index}, this.value)" placeholder="e.g., semantic, conversational">
            </div>
            
            <div class="form-group">
                <label>
                    <input type="checkbox" ${model.nsfw ? 'checked' : ''} onchange="updateModel('${providerKey}', ${index}, 'nsfw', this.checked)">
                    NSFW
                </label>
            </div>
            
            <div class="form-group">
                <label>
                    <input type="checkbox" ${model.privacy ? 'checked' : ''} onchange="updateModel('${providerKey}', ${index}, 'privacy', this.checked)">
                    Privacy
                </label>
            </div>
        `;
        
        container.appendChild(modelDiv);
    });
}

function showAddProviderForm() {
    document.getElementById('new-provider-form').style.display = 'block';
    document.getElementById('add-provider-btn').style.display = 'none';
    document.getElementById('new-provider-key').value = '';
    document.getElementById('new-provider-type').value = 'openai';
    updateNewProviderDefaults();
    document.getElementById('new-provider-key').focus();
}

function cancelAddProvider() {
    document.getElementById('new-provider-form').style.display = 'none';
    document.getElementById('add-provider-btn').style.display = 'inline-block';
}

function updateNewProviderDefaults() {
    const providerType = document.getElementById('new-provider-type').value;
    const descriptionEl = document.getElementById('new-provider-type-description');
    
    const descriptions = {
        'openai': 'OpenAI-compatible provider. Uses standard API key authentication. Endpoint: https://api.openai.com/v1',
        'google': 'Google AI provider (Gemini). Uses API key authentication. Endpoint: https://generativelanguage.googleapis.com/v1beta',
        'anthropic': 'Anthropic provider (Claude). Uses API key authentication. Endpoint: https://api.anthropic.com/v1',
        'ollama': 'Ollama local provider. No API key required by default. Endpoint: http://localhost:11434/api',
        'kiro': 'Kiro (Amazon Q Developer) provider. Uses Kiro credentials (IDE, CLI, or direct tokens). Endpoint: https://q.us-east-1.amazonaws.com',
        'claude': 'Claude Code provider. Uses OAuth2 authentication (browser-based login). Endpoint: https://api.anthropic.com/v1',
        'kilocode': 'Kilocode provider. Uses OAuth2 Device Authorization Grant. Endpoint: https://api.kilo.ai/api/gateway'
    };
    
    descriptionEl.textContent = descriptions[providerType] || 'Standard provider configuration.';
}

function confirmAddProvider() {
    const key = document.getElementById('new-provider-key').value.trim();
    const providerType = document.getElementById('new-provider-type').value;
    
    // Validate key
    if (!key) {
        showFormError('Please enter a provider key');
        document.getElementById('new-provider-key').focus();
        return;
    }
    
    if (providersData[key]) {
        showFormError('A provider with this key already exists');
        document.getElementById('new-provider-key').focus();
        return;
    }
    
    // Validate key format (alphanumeric, dashes, underscores only)
    if (!/^[a-zA-Z0-9_-]+$/.test(key)) {
        showFormError('Provider key can only contain letters, numbers, dashes, and underscores');
        document.getElementById('new-provider-key').focus();
        return;
    }
    
    // Create base provider config
    const newProvider = {
        id: key,
        name: key,
        endpoint: '',
        type: providerType,
        api_key_required: providerType !== 'kiro' && providerType !== 'ollama' && providerType !== 'claude' && providerType !== 'kilocode',
        rate_limit: 0,
        default_rate_limit: 0,
        models: []
    };
    
    // Set type-specific defaults
    if (providerType === 'kiro') {
        newProvider.endpoint = 'https://q.us-east-1.amazonaws.com';
        newProvider.name = key + ' (Amazon Q Developer)';
        newProvider.kiro_config = {
            region: 'us-east-1',
            creds_file: '',
            sqlite_db: '',
            refresh_token: '',
            profile_arn: '',
            client_id: '',
            client_secret: ''
        };
    } else if (providerType === 'claude') {
        newProvider.endpoint = 'https://api.anthropic.com/v1';
        newProvider.name = key + ' (Claude OAuth2)';
        newProvider.claude_config = {
            credentials_file: '~/.claude_credentials.json'
        };
    } else if (providerType === 'kilocode') {
        newProvider.endpoint = 'https://api.kilo.ai/api/gateway';
        newProvider.name = key + ' (Kilocode OAuth2)';
        newProvider.kilo_config = {
            credentials_file: '~/.kilo_credentials.json',
            api_base: 'https://api.kilo.ai/api/gateway'
        };
    } else if (providerType === 'openai') {
        newProvider.endpoint = 'https://api.openai.com/v1';
        newProvider.api_key = '';
    } else if (providerType === 'google') {
        newProvider.endpoint = 'https://generativelanguage.googleapis.com/v1beta';
        newProvider.api_key = '';
    } else if (providerType === 'anthropic') {
        newProvider.endpoint = 'https://api.anthropic.com/v1';
        newProvider.api_key = '';
    } else if (providerType === 'ollama') {
        newProvider.endpoint = 'http://localhost:11434/api';
        newProvider.api_key_required = false;
        newProvider.api_key = '';
    } else {
        newProvider.api_key = '';
    }
    
    providersData[key] = newProvider;
    expandedProviders.add(key);
    
    // Hide the form and show the button
    cancelAddProvider();
    
    renderProvidersList();
    
    // Scroll to the new provider
    setTimeout(() => {
        const providerElement = document.querySelector(`[onclick*="toggleProvider('${key}')"]`);
        if (providerElement) {
            providerElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
    }, 100);
}

function showFormError(message) {
    // Check if error element already exists
    let errorEl = document.getElementById('new-provider-error');
    if (!errorEl) {
        errorEl = document.createElement('div');
        errorEl.id = 'new-provider-error';
        errorEl.className = 'alert alert-error';
        errorEl.style.marginBottom = '15px';
        const form = document.getElementById('new-provider-form');
        form.insertBefore(errorEl, form.querySelector('.form-group'));
    }
    errorEl.textContent = message;
    errorEl.style.display = 'block';
    
    // Auto-hide after 5 seconds
    setTimeout(() => {
        if (errorEl) {
            errorEl.style.display = 'none';
        }
    }, 5000);
}

// Legacy function - kept for compatibility but redirects to new form
function addProvider() {
    showAddProviderForm();
}

function removeProvider(key) {
    if (confirm(`Remove provider "${key}"?`)) {
        delete providersData[key];
        expandedProviders.delete(key);
        renderProvidersList();
    }
}

function updateProvider(key, field, value) {
    providersData[key][field] = value;
}

function updateProviderType(key, newType) {
    const oldType = providersData[key].type;
    providersData[key].type = newType;
    
    // Handle transition to/from kiro type
    if (newType === 'kiro' && oldType !== 'kiro') {
        // Transitioning TO kiro: initialize kiro_config, set api_key_required to false
        providersData[key].api_key_required = false;
        providersData[key].kiro_config = {
            region: 'us-east-1',
            creds_file: '',
            sqlite_db: '',
            refresh_token: '',
            profile_arn: '',
            client_id: '',
            client_secret: ''
        };
        delete providersData[key].claude_config;
        delete providersData[key].kilo_config;
        // Set default endpoint for kiro
        if (!providersData[key].endpoint || providersData[key].endpoint === '') {
            providersData[key].endpoint = 'https://q.us-east-1.amazonaws.com';
        }
    } else if (newType === 'claude' && oldType !== 'claude') {
        // Transitioning TO claude: initialize claude_config, set api_key_required to false
        providersData[key].api_key_required = false;
        providersData[key].claude_config = {
            credentials_file: '~/.claude_credentials.json'
        };
        delete providersData[key].kiro_config;
        delete providersData[key].kilo_config;
        // Set default endpoint for claude
        if (!providersData[key].endpoint || providersData[key].endpoint === '') {
            providersData[key].endpoint = 'https://api.anthropic.com/v1';
        }
    } else if (newType === 'kilocode' && oldType !== 'kilocode') {
        // Transitioning TO kilocode: initialize kilo_config, set api_key_required to false
        providersData[key].api_key_required = false;
        providersData[key].kilo_config = {
            credentials_file: '~/.kilo_credentials.json',
            api_base: 'https://api.kilo.ai/api/gateway'
        };
        delete providersData[key].kiro_config;
        delete providersData[key].claude_config;
        // Set endpoint for kilocode (fixed, not modifiable)
        providersData[key].endpoint = 'https://api.kilo.ai/api/gateway';
    } else if (newType !== 'kiro' && newType !== 'claude' && newType !== 'kilocode' && (oldType === 'kiro' || oldType === 'claude' || oldType === 'kilocode')) {
        // Transitioning FROM kiro/claude/kilocode: remove special configs, set api_key_required to true
        providersData[key].api_key_required = true;
        delete providersData[key].kiro_config;
        delete providersData[key].claude_config;
        delete providersData[key].kilo_config;
    }
    
    // Re-render to show appropriate fields
    renderProviderDetails(key);
}

function updateKiroConfig(key, field, value) {
    if (!providersData[key].kiro_config) {
        providersData[key].kiro_config = {};
    }
    providersData[key].kiro_config[field] = value;
}

function updateClaudeConfig(key, field, value) {
    if (!providersData[key].claude_config) {
        providersData[key].claude_config = {};
    }
    providersData[key].claude_config[field] = value;
}

function updateKiloConfig(key, field, value) {
    if (!providersData[key].kilo_config) {
        providersData[key].kilo_config = {};
    }
    providersData[key].kilo_config[field] = value;
}

async function authenticateKilo(key) {
    const statusEl = document.getElementById(`kilo-auth-status-${key}`);
    statusEl.style.display = 'block';
    statusEl.style.background = '#0f2840';
    statusEl.style.border = '1px solid #4a9eff';
    statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Starting OAuth2 Device Authorization flow...</p>';
    
    try {
        const response = await fetch('/dashboard/kilo/auth/start', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                provider_key: key,
                credentials_file: providersData[key].kilo_config?.credentials_file || '~/.kilo_credentials.json',
                api_base: providersData[key].kilo_config?.api_base || 'https://api.kilo.ai'
            })
        });
        
        const data = await response.json();
        
        if (!data.success) {
            statusEl.style.background = '#402010';
            statusEl.style.border = '1px solid #ff4a4a';
            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Failed to start authentication: ${data.error || 'Unknown error'}</p>`;
            return;
        }
        
        // Display the verification URL and code to the user
        statusEl.style.background = '#0f2840';
        statusEl.style.border = '1px solid #4a9eff';
        statusEl.innerHTML = `
            <div style="margin: 0;">
                <p style="margin: 0 0 10px 0; color: #4a9eff; font-weight: bold;">🔐 Authorization Required</p>
                <p style="margin: 0 0 10px 0; color: #e0e0e0;">
                    Please visit: <a href="${data.verification_uri}" target="_blank" style="color: #4eff9e; text-decoration: underline;">${data.verification_uri}</a>
                </p>
                <p style="margin: 0 0 10px 0; color: #e0e0e0;">
                    Enter code: <strong style="color: #4eff9e; font-size: 18px; letter-spacing: 2px;">${data.user_code}</strong>
                </p>
                <p style="margin: 0; color: #a0a0a0; font-size: 13px;">
                    Waiting for authorization... (expires in ${Math.floor(data.expires_in / 60)} minutes)
                </p>
            </div>
        `;
        
        // Open the verification URL in a new window
        try {
            window.open(data.verification_uri, 'kilo-auth', 'width=600,height=700');
        } catch (e) {
            console.error('Could not open auth window:', e);
        }
        
        // Poll for authorization completion
        let pollCount = 0;
        const maxPolls = Math.floor(data.expires_in / data.interval);
        const pollInterval = setInterval(async () => {
            pollCount++;
            
            try {
                const pollResponse = await fetch('/dashboard/kilo/auth/poll', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    }
                });
                
                const pollData = await pollResponse.json();
                
                if (pollData.status === 'approved') {
                    clearInterval(pollInterval);
                    statusEl.style.background = '#0f4020';
                    statusEl.style.border = '1px solid #4eff9e';
                    statusEl.innerHTML = '<p style="margin: 0; color: #4eff9e;">✓ Authentication successful! Credentials saved.</p>';
                } else if (pollData.status === 'denied') {
                    clearInterval(pollInterval);
                    statusEl.style.background = '#402010';
                    statusEl.style.border = '1px solid #ff4a4a';
                    statusEl.innerHTML = '<p style="margin: 0; color: #ff4a4a;">✗ Authorization denied by user.</p>';
                } else if (pollData.status === 'expired') {
                    clearInterval(pollInterval);
                    statusEl.style.background = '#402010';
                    statusEl.style.border = '1px solid #ff4a4a';
                    statusEl.innerHTML = '<p style="margin: 0; color: #ff4a4a;">✗ Authorization code expired. Please try again.</p>';
                } else if (pollData.status === 'slow_down') {
                    // Server asked us to slow down, but we'll continue at the same rate
                    console.log('Server requested slow down');
                }
                // status === 'pending', continue polling
                
            } catch (error) {
                console.error('Error polling Kilo auth:', error);
            }
            
            // Timeout after max polls
            if (pollCount >= maxPolls) {
                clearInterval(pollInterval);
                statusEl.style.background = '#402010';
                statusEl.style.border = '1px solid #ff4a4a';
                statusEl.innerHTML = '<p style="margin: 0; color: #ff4a4a;">✗ Authentication timeout. Please try again.</p>';
            }
        }, data.interval * 1000); // Convert seconds to milliseconds
        
    } catch (error) {
        statusEl.style.background = '#402010';
        statusEl.style.border = '1px solid #ff4a4a';
        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error: ${error.message}</p>`;
    }
}

async function checkKiloAuth(key) {
    const statusEl = document.getElementById(`kilo-auth-status-${key}`);
    statusEl.style.display = 'block';
    statusEl.style.background = '#0f2840';
    statusEl.style.border = '1px solid #4a9eff';
    statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Checking authentication status...</p>';
    
    try {
        const response = await fetch('/dashboard/kilo/auth/status', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                provider_key: key,
                credentials_file: providersData[key].kilo_config?.credentials_file || '~/.kilo_credentials.json'
            })
        });
        
        const data = await response.json();
        
        if (data.authenticated) {
            statusEl.style.background = '#0f4020';
            statusEl.style.border = '1px solid #4eff9e';
            const expiresIn = data.expires_in ? ` (expires in ${Math.floor(data.expires_in / (24 * 60 * 60))} days)` : '';
            statusEl.innerHTML = `<p style="margin: 0; color: #4eff9e;">✓ Authenticated${expiresIn}</p>`;
        } else {
            statusEl.style.background = '#402010';
            statusEl.style.border = '1px solid #ff4a4a';
            statusEl.innerHTML = '<p style="margin: 0; color: #ff4a4a;">✗ Not authenticated. Click "Authenticate with Kilo" to log in.</p>';
        }
    } catch (error) {
        statusEl.style.background = '#402010';
        statusEl.style.border = '1px solid #ff4a4a';
        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error: ${error.message}</p>`;
    }
}

async function uploadKiloFile(providerKey, file) {
    if (!file) return;
    
    const statusEl = document.getElementById(`kilo-upload-status-${providerKey}`);
    statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Uploading file...</p>';
    
    const formData = new FormData();
    formData.append('file', file);
    formData.append('provider_key', providerKey);
    formData.append('file_type', 'credentials');
    
    try {
        const response = await fetch('/dashboard/providers/upload-auth-file', {
            method: 'POST',
            body: formData
        });
        
        const data = await response.json();
        
        if (data.success) {
            statusEl.innerHTML = `<p style="margin: 0; color: #4eff9e;">✓ File uploaded successfully! Path: ${data.file_path}</p>`;
            // Update the config with the new file path
            updateKiloConfig(providerKey, 'credentials_file', data.file_path);
        } else {
            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Upload failed: ${data.error}</p>`;
        }
    } catch (error) {
        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error: ${error.message}</p>`;
    }
}

// Extension detection and configuration
let extensionInstalled = false;
let extensionCheckComplete = false;

async function checkExtensionInstalled() {
    if (extensionCheckComplete) {
        return extensionInstalled;
    }
    
    try {
        // Check if the extension has injected a marker into the page
        // The extension should set window.aisbfOAuth2Extension when loaded
        if (window.aisbfOAuth2Extension) {
            extensionInstalled = true;
            extensionCheckComplete = true;
            return true;
        }
        
        // Try using postMessage to detect the extension
        return new Promise((resolve) => {
            const messageHandler = (event) => {
                if (event.data && event.data.type === 'aisbf-extension-pong') {
                    extensionInstalled = true;
                    extensionCheckComplete = true;
                    window.removeEventListener('message', messageHandler);
                    resolve(true);
                }
            };
            
            window.addEventListener('message', messageHandler);
            
            // Send ping message
            window.postMessage({ type: 'aisbf-extension-ping' }, '*');
            
            // Timeout after 1 second
            setTimeout(() => {
                window.removeEventListener('message', messageHandler);
                extensionInstalled = false;
                extensionCheckComplete = true;
                resolve(false);
            }, 1000);
        });
    } catch (error) {
        console.error('Error checking extension:', error);
        extensionInstalled = false;
        extensionCheckComplete = true;
        return false;
    }
}

async function configureExtension() {
    try {
        const serverUrl = window.location.origin;
        
        // Send configuration to extension using postMessage
        window.postMessage({
            type: 'aisbf-extension-configure',
            serverUrl: serverUrl
        }, '*');
        
        console.log('Extension configuration sent');
    } catch (error) {
        console.error('Error configuring extension:', error);
    }
}

async function authenticateClaude(key) {
    const statusEl = document.getElementById(`claude-auth-status-${key}`);
    statusEl.style.display = 'block';
    statusEl.style.background = '#0f2840';
    statusEl.style.border = '1px solid #4a9eff';
    statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Starting OAuth2 authentication flow...</p>';
    
    try {
        // Get OAuth2 URL from server
        const response = await fetch('/dashboard/claude/auth/start', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                provider_key: key,
                credentials_file: providersData[key].claude_config?.credentials_file || '~/.claude_credentials.json'
            })
        });
        
        const data = await response.json();
        
        if (!data.success || !data.auth_url) {
            statusEl.style.background = '#402010';
            statusEl.style.border = '1px solid #ff4a4a';
            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Failed to start authentication: ${data.error || 'Unknown error'}</p>`;
            return;
        }
        
        // Check if extension is needed (not needed when accessing from localhost)
        const needsExtension = data.use_extension;
        
        if (needsExtension) {
            // Check if extension is installed
            const hasExtension = await checkExtensionInstalled();
            
            if (!hasExtension) {
                // Show extension installation prompt
                statusEl.style.background = '#403010';
                statusEl.style.border = '1px solid #ffaa4a';
                statusEl.innerHTML = `
                    <div style="margin: 0;">
                        <p style="margin: 0 0 10px 0; color: #ffaa4a;">⚠️ OAuth2 Redirect Extension Required</p>
                        <p style="margin: 0 0 10px 0; color: #a0a0a0; font-size: 13px;">
                            For remote server OAuth2 authentication, you need to install the AISBF OAuth2 Redirect Extension.
                            This extension intercepts localhost OAuth2 callbacks and redirects them to your AISBF server.
                        </p>
                        <div style="display: flex; gap: 10px; margin-top: 10px;">
                            <a href="/dashboard/extension/download" class="btn" style="background: #4a9eff; color: white; text-decoration: none; padding: 8px 15px; border-radius: 3px; font-size: 13px;">
                                📥 Download Extension
                            </a>
                            <button type="button" class="btn btn-secondary" onclick="showExtensionInstructions()" style="padding: 8px 15px; font-size: 13px;">
                                📖 Installation Guide
                            </button>
                        </div>
                    </div>
                `;
                return;
            }
            
            // Configure extension with server URL
            await configureExtension();
        }
        
        statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Opening authentication window...</p>';
        
        // Open OAuth2 URL in new window
        const authWindow = window.open(data.auth_url, 'claude-auth', 'width=600,height=700');
        
        // Wait for user to interact with the popup before starting to poll
        statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Please complete authentication in the popup window...</p>';
        
        // Poll for completion
        let pollCount = 0;
        const maxPolls = 60; // 2 minutes
        let pollDelay = 2000; // 2 second delay between polls
        let isCompleting = false; // Prevent duplicate complete calls
        
        // Wait 8 seconds before starting to poll (give user time to interact with popup)
        await new Promise(resolve => setTimeout(resolve, 8000));
        
        const pollInterval = setInterval(async () => {
            pollCount++;
            
            // For localhost flow, check callback status endpoint
            if (!needsExtension) {
                try {
                    const statusResponse = await fetch('/dashboard/claude/auth/callback-status');
                    const statusData = await statusResponse.json();
                    
                    if (statusData.received) {
                        clearInterval(pollInterval);
                        
                        // Close the auth window if still open
                        if (authWindow && !authWindow.closed) {
                            authWindow.close();
                        }
                        
                        if (statusData.error) {
                            statusEl.style.background = '#402010';
                            statusEl.style.border = '1px solid #ff4a4a';
                            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ OAuth2 error: ${statusData.error}</p>`;
                            return;
                        }
                        
                        // Prevent duplicate complete calls
                        if (isCompleting) return;
                        isCompleting = true;
                        
                        // Wait a moment before completing to avoid rate limits
                        await new Promise(resolve => setTimeout(resolve, 1000));
                        
                        // Try to complete authentication
                        try {
                            statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Completing authentication (this may take a moment if rate limited)...</p>';
                            
                            const completeResponse = await fetch('/dashboard/claude/auth/complete', {
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/json',
                                }
                            });
                            
                            const completeData = await completeResponse.json();
                            
                            if (completeData.success) {
                                statusEl.style.background = '#0f4020';
                                statusEl.style.border = '1px solid #4eff9e';
                                statusEl.innerHTML = '<p style="margin: 0; color: #4eff9e;">✓ Authentication successful! Credentials saved.</p>';
                            } else {
                                statusEl.style.background = '#402010';
                                statusEl.style.border = '1px solid #ff4a4a';
                                if (completeData.error && completeData.error.includes('rate_limit')) {
                                    statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Rate limited by Claude. Please wait 1-2 minutes and try again.</p>`;
                                } else {
                                    statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Authentication incomplete. ${completeData.error || 'Please try again.'}</p>`;
                                }
                            }
                        } catch (error) {
                            statusEl.style.background = '#402010';
                            statusEl.style.border = '1px solid #ff4a4a';
                            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error completing authentication: ${error.message}</p>`;
                        }
                    }
                } catch (error) {
                    console.error('Error checking callback status:', error);
                    // Increase delay on error to avoid rate limits
                    pollDelay = Math.min(pollDelay * 1.5, 10000);
                }
            } else {
                // For extension flow, check if window was closed
                if (authWindow && authWindow.closed) {
                    clearInterval(pollInterval);
                    
                    // Prevent duplicate complete calls
                    if (isCompleting) return;
                    isCompleting = true;
                    
                    // Wait a moment for the OAuth2 callback to be processed
                    await new Promise(resolve => setTimeout(resolve, 2000));
                    
                    // Try to complete authentication
                    try {
                        const completeResponse = await fetch('/dashboard/claude/auth/complete', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                            }
                        });
                        
                        const completeData = await completeResponse.json();
                        
                        if (completeData.success) {
                            statusEl.style.background = '#0f4020';
                            statusEl.style.border = '1px solid #4eff9e';
                            statusEl.innerHTML = '<p style="margin: 0; color: #4eff9e;">✓ Authentication successful! Credentials saved.</p>';
                        } else {
                            statusEl.style.background = '#402010';
                            statusEl.style.border = '1px solid #ff4a4a';
                            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Authentication incomplete. ${completeData.error || 'Please try again.'}</p>`;
                        }
                    } catch (error) {
                        statusEl.style.background = '#402010';
                        statusEl.style.border = '1px solid #ff4a4a';
                        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error completing authentication: ${error.message}</p>`;
                    }
                }
            }
            
            // Timeout after max polls
            if (pollCount >= maxPolls) {
                clearInterval(pollInterval);
                if (authWindow && !authWindow.closed) {
                    authWindow.close();
                }
                statusEl.style.background = '#402010';
                statusEl.style.border = '1px solid #ff4a4a';
                statusEl.innerHTML = '<p style="margin: 0; color: #ff4a4a;">✗ Authentication timeout. Please try again.</p>';
            }
        }, pollDelay); // Use dynamic delay
        
    } catch (error) {
        statusEl.style.background = '#402010';
        statusEl.style.border = '1px solid #ff4a4a';
        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error: ${error.message}</p>`;
    }
}

function showExtensionInstructions() {
    // Create modal overlay
    const modal = document.createElement('div');
    modal.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.8);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 10000;
        padding: 20px;
    `;
    
    modal.innerHTML = `
        <div style="background: #16213e; border: 2px solid #4a9eff; border-radius: 10px; padding: 30px; max-width: 700px; max-height: 90vh; overflow-y: auto;">
            <h2 style="margin: 0 0 20px 0; color: #4a9eff; font-size: 24px;">
                📦 AISBF OAuth2 Extension - Installation Guide
            </h2>
            
            <div style="background: #0f2840; padding: 15px; border-radius: 5px; margin-bottom: 20px; border-left: 3px solid #4eff9e;">
                <p style="margin: 0; color: #4eff9e; font-weight: bold;">ℹ️ About This Extension</p>
                <p style="margin: 5px 0 0 0; color: #a0a0a0; font-size: 14px;">
                    This extension intercepts OAuth2 callbacks from localhost:54545 and redirects them to your AISBF server, enabling remote authentication.
                    <p style="margin: 5px 0 0 0; color: #a0a0a0; font-size: 14px;">
                        <strong>Proxy Support:</strong> The extension automatically detects reverse proxy deployments (nginx, caddy, etc.) via X-Forwarded-* headers and activates when OAuth flows are initiated from the dashboard.
                    </p>
                </p>
            </div>
            
            <div style="margin-bottom: 25px;">
                <h3 style="color: #8ec8ff; margin: 0 0 15px 0; font-size: 18px;">Installation Steps:</h3>
                
                <div style="margin-bottom: 20px;">
                    <div style="display: flex; align-items: start; margin-bottom: 15px;">
                        <div style="background: #4a9eff; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; margin-right: 15px;">1</div>
                        <div>
                            <strong style="color: #e0e0e0; display: block; margin-bottom: 5px;">Download the Extension</strong>
                            <p style="margin: 0; color: #a0a0a0; font-size: 14px;">
                                Click the "📥 Download Extension" button to download <code style="background: #0f2840; padding: 2px 6px; border-radius: 3px;">aisbf-oauth2-extension.zip</code>
                            </p>
                        </div>
                    </div>
                    
                    <div style="display: flex; align-items: start; margin-bottom: 15px;">
                        <div style="background: #4a9eff; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; margin-right: 15px;">2</div>
                        <div>
                            <strong style="color: #e0e0e0; display: block; margin-bottom: 5px;">Extract the ZIP File</strong>
                            <p style="margin: 0; color: #a0a0a0; font-size: 14px;">
                                Extract the contents to a <strong>permanent location</strong> (e.g., Documents/Extensions/). Don't delete this folder after installation.
                            </p>
                        </div>
                    </div>
                    
                    <div style="display: flex; align-items: start; margin-bottom: 15px;">
                        <div style="background: #4a9eff; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; margin-right: 15px;">3</div>
                        <div>
                            <strong style="color: #e0e0e0; display: block; margin-bottom: 5px;">Open Chrome Extensions</strong>
                            <p style="margin: 0; color: #a0a0a0; font-size: 14px;">
                                In Chrome, navigate to: <code style="background: #0f2840; padding: 2px 6px; border-radius: 3px;">chrome://extensions/</code>
                                <br><span style="font-size: 12px; color: #808080;">(Copy and paste this into your address bar)</span>
                            </p>
                        </div>
                    </div>
                    
                    <div style="display: flex; align-items: start; margin-bottom: 15px;">
                        <div style="background: #4a9eff; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; margin-right: 15px;">4</div>
                        <div>
                            <strong style="color: #e0e0e0; display: block; margin-bottom: 5px;">Enable Developer Mode</strong>
                            <p style="margin: 0; color: #a0a0a0; font-size: 14px;">
                                Toggle the "Developer mode" switch in the top-right corner of the extensions page.
                            </p>
                        </div>
                    </div>
                    
                    <div style="display: flex; align-items: start; margin-bottom: 15px;">
                        <div style="background: #4a9eff; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; margin-right: 15px;">5</div>
                        <div>
                            <strong style="color: #e0e0e0; display: block; margin-bottom: 5px;">Load Unpacked Extension</strong>
                            <p style="margin: 0; color: #a0a0a0; font-size: 14px;">
                                Click the "Load unpacked" button and select the <strong>extracted folder</strong> (not the ZIP file).
                            </p>
                        </div>
                    </div>
                    
                    <div style="display: flex; align-items: start; margin-bottom: 15px;">
                        <div style="background: #4a9eff; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; margin-right: 15px;">6</div>
                        <div>
                            <strong style="color: #e0e0e0; display: block; margin-bottom: 5px;">Verify Installation</strong>
                            <p style="margin: 0; color: #a0a0a0; font-size: 14px;">
                                The extension should appear in your extensions list. You may see the extension icon in your browser toolbar.
                            </p>
                        </div>
                    </div>
                    
                    <div style="display: flex; align-items: start;">
                        <div style="background: #4eff9e; color: #0a0a0a; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; flex-shrink: 0; margin-right: 15px;">✓</div>
                        <div>
                            <strong style="color: #4eff9e; display: block; margin-bottom: 5px;">Ready to Authenticate</strong>
                            <p style="margin: 0; color: #a0a0a0; font-size: 14px;">
                                Return to this page and click "🔐 Authenticate with Claude" again. The extension will automatically handle OAuth2 callbacks.
                            </p>
                        </div>
                    </div>
                </div>
            </div>
            
            <div style="background: #0f2840; padding: 15px; border-radius: 5px; margin-bottom: 20px; border-left: 3px solid #ffaa4a;">
                <p style="margin: 0 0 5px 0; color: #ffaa4a; font-weight: bold;">⚠️ Important Notes:</p>
                <ul style="margin: 5px 0 0 20px; padding: 0; color: #a0a0a0; font-size: 13px;">
                    <li style="margin-bottom: 5px;">Extract to a permanent location - don't delete the folder after installation</li>
                    <li style="margin-bottom: 5px;">The extension only intercepts localhost:54545 OAuth2 callbacks</li>
                    <li style="margin-bottom: 5px;">No data is collected or stored by the extension</li>
                    <li>You can disable the extension when not using Claude authentication</li>
                </ul>
            </div>
            
            <div style="display: flex; gap: 10px; justify-content: flex-end;">
                <button type="button" class="btn" onclick="this.closest('div').parentElement.parentElement.remove()" style="background: #4a9eff; padding: 10px 20px;">
                    Got It!
                </button>
            </div>
        </div>
    `;
    
    // Close modal when clicking outside
    modal.addEventListener('click', (e) => {
        if (e.target === modal) {
            modal.remove();
        }
    });
    
    document.body.appendChild(modal);
}

async function checkClaudeAuth(key) {
    const statusEl = document.getElementById(`claude-auth-status-${key}`);
    statusEl.style.display = 'block';
    statusEl.style.background = '#0f2840';
    statusEl.style.border = '1px solid #4a9eff';
    statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Checking authentication status...</p>';
    
    try {
        const response = await fetch('/dashboard/claude/auth/status', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                provider_key: key,
                credentials_file: providersData[key].claude_config?.credentials_file || '~/.claude_credentials.json'
            })
        });
        
        const data = await response.json();
        
        if (data.authenticated) {
            statusEl.style.background = '#0f4020';
            statusEl.style.border = '1px solid #4eff9e';
            const expiresIn = data.expires_in ? ` (expires in ${Math.floor(data.expires_in / 60)} minutes)` : '';
            statusEl.innerHTML = `<p style="margin: 0; color: #4eff9e;">✓ Authenticated${expiresIn}</p>`;
        } else {
            statusEl.style.background = '#402010';
            statusEl.style.border = '1px solid #ff4a4a';
            statusEl.innerHTML = '<p style="margin: 0; color: #ff4a4a;">✗ Not authenticated. Click "Authenticate with Claude" to log in.</p>';
        }
    } catch (error) {
        statusEl.style.background = '#402010';
        statusEl.style.border = '1px solid #ff4a4a';
        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error: ${error.message}</p>`;
    }
}

function addModel(providerKey) {
    if (!providersData[providerKey].models) {
        providersData[providerKey].models = [];
    }
    
    providersData[providerKey].models.push({
        name: '',
        rate_limit: 0
    });
    
    renderModels(providerKey);
}

function removeModel(providerKey, index) {
    if (confirm('Remove this model?')) {
        providersData[providerKey].models.splice(index, 1);
        renderModels(providerKey);
    }
}

function updateModel(providerKey, index, field, value) {
    providersData[providerKey].models[index][field] = value;
}

function updateModelCondenseMethod(providerKey, index, value) {
    const trimmed = value.trim();
    if (!trimmed) {
        providersData[providerKey].models[index].condense_method = null;
        return;
    }
    
    // Check if it's a comma-separated list
    if (trimmed.includes(',')) {
        providersData[providerKey].models[index].condense_method =
            trimmed.split(',').map(s => s.trim()).filter(s => s);
    } else {
        providersData[providerKey].models[index].condense_method = trimmed;
    }
}

function updateProviderCondenseMethod(providerKey, value) {
    const trimmed = value.trim();
    if (!trimmed) {
        providersData[providerKey].default_condense_method = null;
        return;
    }
    
    // Check if it's a comma-separated list
    if (trimmed.includes(',')) {
        providersData[providerKey].default_condense_method =
            trimmed.split(',').map(s => s.trim()).filter(s => s);
    } else {
        providersData[providerKey].default_condense_method = trimmed;
    }
}

async function saveProviders() {
    try {
        const response = await fetch('/dashboard/providers', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: 'config=' + encodeURIComponent(JSON.stringify(providersData, null, 2))
        });
        
        if (response.ok) {
            window.location.href = '/dashboard/providers?success=1';
        } else {
            alert('Error saving configuration');
        }
    } catch (error) {
        alert('Error: ' + error.message);
    }
}

async function uploadKiroFile(providerKey, fileType, file) {
    if (!file) return;
    
    const statusEl = document.getElementById(`kiro-upload-status-${providerKey}`);
    statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Uploading file...</p>';
    
    const formData = new FormData();
    formData.append('file', file);
    formData.append('provider_key', providerKey);
    formData.append('file_type', fileType);
    
    try {
        const response = await fetch('/dashboard/providers/upload-auth-file', {
            method: 'POST',
            body: formData
        });
        
        const data = await response.json();
        
        if (data.success) {
            statusEl.innerHTML = `<p style="margin: 0; color: #4eff9e;">✓ File uploaded successfully! Path: ${data.file_path}</p>`;
            // Update the config with the new file path
            if (fileType === 'creds_file') {
                updateKiroConfig(providerKey, 'creds_file', data.file_path);
            } else if (fileType === 'sqlite_db') {
                updateKiroConfig(providerKey, 'sqlite_db', data.file_path);
            }
        } else {
            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Upload failed: ${data.error}</p>`;
        }
    } catch (error) {
        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error: ${error.message}</p>`;
    }
}

async function uploadClaudeFile(providerKey, file) {
    if (!file) return;
    
    const statusEl = document.getElementById(`claude-upload-status-${providerKey}`);
    statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Uploading file...</p>';
    
    const formData = new FormData();
    formData.append('file', file);
    formData.append('provider_key', providerKey);
    formData.append('file_type', 'credentials');
    
    try {
        const response = await fetch('/dashboard/providers/upload-auth-file', {
            method: 'POST',
            body: formData
        });
        
        const data = await response.json();
        
        if (data.success) {
            statusEl.innerHTML = `<p style="margin: 0; color: #4eff9e;">✓ File uploaded successfully! Path: ${data.file_path}</p>`;
            // Update the config with the new file path
            updateClaudeConfig(providerKey, 'credentials_file', data.file_path);
        } else {
            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Upload failed: ${data.error}</p>`;
        }
    } catch (error) {
        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error: ${error.message}</p>`;
    }
}

async function getModelsFromProvider(providerKey) {
    const statusEl = document.getElementById(`get-models-status-${providerKey}`);
    statusEl.style.display = 'block';
    statusEl.style.background = '#0f2840';
    statusEl.style.border = '1px solid #4a9eff';
    statusEl.style.padding = '10px';
    statusEl.style.borderRadius = '3px';
    statusEl.innerHTML = '<p style="margin: 0; color: #4a9eff;">🔄 Fetching models from provider API...</p>';
    
    try {
        const response = await fetch('/dashboard/providers/get-models', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                provider_key: providerKey
            })
        });
        
        const data = await response.json();
        
        if (data.success && data.models && data.models.length > 0) {
            // Clear existing models
            providersData[providerKey].models = [];
            
            // Add fetched models with all available fields
            data.models.forEach(model => {
                const modelConfig = {
                    name: model.id || model.name,
                    rate_limit: 0,
                    max_request_tokens: null,
                    context_size: model.context_size || model.context_length || null,
                    rate_limit_TPM: null,
                    rate_limit_TPH: null,
                    rate_limit_TPD: null,
                    condense_context: null,
                    condense_method: null,
                    nsfw: false,
                    privacy: false
                };
                
                // Add optional fields if available from API
                if (model.description) {
                    modelConfig.description = model.description;
                }
                if (model.context_length) {
                    modelConfig.context_length = model.context_length;
                }
                if (model.architecture) {
                    modelConfig.architecture = model.architecture;
                }
                if (model.pricing) {
                    modelConfig.pricing = model.pricing;
                }
                if (model.top_provider) {
                    modelConfig.top_provider = model.top_provider;
                }
                if (model.supported_parameters) {
                    modelConfig.supported_parameters = model.supported_parameters;
                }
                if (model.default_parameters) {
                    modelConfig.default_parameters = model.default_parameters;
                }
                
                providersData[providerKey].models.push(modelConfig);
            });
            
            // Re-render models
            renderModels(providerKey);
            
            statusEl.style.background = '#0f4020';
            statusEl.style.border = '1px solid #4eff9e';
            statusEl.innerHTML = `<p style="margin: 0; color: #4eff9e;">✓ Successfully fetched ${data.models.length} model(s)! Review and save the configuration.</p>`;
            
            // Auto-hide success message after 5 seconds
            setTimeout(() => {
                statusEl.style.display = 'none';
            }, 5000);
        } else if (data.success && (!data.models || data.models.length === 0)) {
            statusEl.style.background = '#403010';
            statusEl.style.border = '1px solid #ffaa4a';
            statusEl.innerHTML = '<p style="margin: 0; color: #ffaa4a;">⚠️ No models found from provider API.</p>';
        } else {
            statusEl.style.background = '#402010';
            statusEl.style.border = '1px solid #ff4a4a';
            statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Failed to fetch models: ${data.error || 'Unknown error'}</p>`;
        }
    } catch (error) {
        statusEl.style.background = '#402010';
        statusEl.style.border = '1px solid #ff4a4a';
        statusEl.innerHTML = `<p style="margin: 0; color: #ff4a4a;">✗ Error: ${error.message}</p>`;
    }
}

// Initial render
renderProvidersList();
</script>

<style>
.provider-item {
    animation: fadeIn 0.3s;
}

.provider-header:hover {
    background: #0f3460;
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(-10px); }
    to { opacity: 1; transform: translateY(0); }
}

.form-group {
    margin-bottom: 15px;
}

.form-group label {
    display: block;
    margin-bottom: 5px;
    font-weight: 500;
    color: #e0e0e0;
}

.form-group input[type="text"],
.form-group input[type="password"],
.form-group input[type="number"],
.form-group select {
    width: 100%;
    padding: 8px;
    border: 1px solid #0f3460;
    border-radius: 3px;
    font-size: 14px;
    background: #1a1a2e;
    color: #e0e0e0;
}

.form-group input[type="checkbox"] {
    margin-right: 5px;
}
</style>
{% endblock %}
