"use strict";
/**
 * Account Routes - CRUD operations for Claude accounts
 *
 * Uses ProfileRegistry to read from both legacy (profiles.json)
 * and unified config (config.yaml) for consistent data with CLI.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const profile_registry_1 = __importDefault(require("../../auth/profile-registry"));
const instance_manager_1 = __importDefault(require("../../management/instance-manager"));
const account_manager_1 = require("../../cliproxy/accounts/account-manager");
const email_account_identity_1 = require("../../cliproxy/accounts/email-account-identity");
const provider_capabilities_1 = require("../../cliproxy/provider-capabilities");
const account_context_1 = require("../../auth/account-context");
const shared_resource_policy_1 = require("../../auth/shared-resource-policy");
const account_route_helpers_1 = require("./account-route-helpers");
const resume_lane_diagnostics_1 = require("../../auth/resume-lane-diagnostics");
const config_loader_facade_1 = require("../../config/config-loader-facade");
const router = (0, express_1.Router)();
function createProfileRegistry() {
    return new profile_registry_1.default();
}
function createInstanceManager() {
    return new instance_manager_1.default();
}
function getUnifiedAccountsRaw() {
    if (!(0, config_loader_facade_1.isUnifiedMode)()) {
        return {};
    }
    return (0, config_loader_facade_1.loadOrCreateUnifiedConfig)().accounts;
}
function hasAuthAccount(name) {
    const registry = createProfileRegistry();
    return registry.hasAccountUnified(name) || registry.hasProfile(name);
}
/**
 * GET /api/accounts - List accounts from both profiles.json and config.yaml
 */
router.get('/', async (_req, res) => {
    try {
        const registry = createProfileRegistry();
        // Get profiles from both legacy and unified config (same logic as CLI)
        const legacyProfiles = registry.getAllProfiles();
        const rawUnifiedAccounts = getUnifiedAccountsRaw();
        const unifiedAccounts = registry.getAllAccountsUnified();
        // Get CLIProxy OAuth accounts (gemini, codex, agy, etc.)
        const cliproxyAccounts = (0, account_manager_1.getAllAccountsSummary)();
        // Merge profiles: unified config takes precedence
        const merged = {};
        // Add legacy profiles first
        for (const [name, meta] of Object.entries(legacyProfiles)) {
            const contextPolicy = (0, account_context_1.resolveAccountContextPolicy)(meta);
            const resourcePolicy = (0, shared_resource_policy_1.resolveSharedResourcePolicy)(meta);
            const hasExplicitContextMode = meta.context_mode === 'isolated' || meta.context_mode === 'shared';
            const hasExplicitContinuityMode = meta.continuity_mode === 'standard' || meta.continuity_mode === 'deeper';
            merged[name] = {
                type: meta.type || 'account',
                created: meta.created,
                last_used: meta.last_used || null,
                context_mode: contextPolicy.mode,
                context_group: contextPolicy.group,
                continuity_mode: contextPolicy.mode === 'shared' ? contextPolicy.continuityMode : undefined,
                context_inferred: !hasExplicitContextMode,
                continuity_inferred: contextPolicy.mode === 'shared' ? !hasExplicitContinuityMode : undefined,
                shared_resource_mode: resourcePolicy.mode,
                shared_resource_inferred: resourcePolicy.inferred,
                ...(resourcePolicy.profileLocal ? { bare: true } : {}),
            };
        }
        // Override with unified config accounts (takes precedence)
        for (const [name, account] of Object.entries(unifiedAccounts)) {
            const rawAccount = rawUnifiedAccounts[name];
            const contextPolicy = (0, account_context_1.resolveAccountContextPolicy)(account);
            const resourcePolicy = (0, shared_resource_policy_1.resolveSharedResourcePolicy)(account);
            const hasExplicitContextMode = rawAccount?.context_mode === 'isolated' || rawAccount?.context_mode === 'shared';
            const hasExplicitContinuityMode = rawAccount?.continuity_mode === 'standard' || rawAccount?.continuity_mode === 'deeper';
            merged[name] = {
                type: 'account',
                created: account.created,
                last_used: account.last_used,
                context_mode: contextPolicy.mode,
                context_group: contextPolicy.group,
                continuity_mode: contextPolicy.mode === 'shared' ? contextPolicy.continuityMode : undefined,
                context_inferred: !hasExplicitContextMode,
                continuity_inferred: contextPolicy.mode === 'shared' ? !hasExplicitContinuityMode : undefined,
                shared_resource_mode: resourcePolicy.mode,
                shared_resource_inferred: resourcePolicy.inferred,
                ...(resourcePolicy.profileLocal ? { bare: true } : {}),
            };
        }
        // Add CLIProxy OAuth accounts
        for (const [provider, accounts] of Object.entries(cliproxyAccounts)) {
            for (const acct of accounts) {
                // Skip accounts with no valid identifier
                if (!acct.id) {
                    continue;
                }
                // Use unique ID for key to prevent collisions between accounts with same nickname/email
                const displayName = acct.nickname || (0, email_account_identity_1.formatAccountDisplayName)(acct);
                const rawKey = `${provider}:${acct.id}`;
                const key = (0, account_route_helpers_1.buildCliproxyAccountKey)(rawKey, merged);
                if (!key) {
                    continue;
                }
                merged[key] = {
                    type: 'cliproxy',
                    provider,
                    displayName,
                    created: acct.createdAt || new Date().toISOString(),
                    last_used: null,
                };
            }
        }
        // Convert to array format
        const accounts = Object.entries(merged).map(([name, meta]) => ({
            name,
            ...meta,
        }));
        // Get default from unified config first, fallback to legacy
        const defaultProfile = registry.getDefaultUnified() ?? registry.getDefaultProfile() ?? null;
        const plainCcsLane = await (0, resume_lane_diagnostics_1.resolveConfiguredPlainCcsResumeLane)();
        res.json({
            accounts,
            default: defaultProfile,
            plain_ccs_lane: {
                kind: plainCcsLane.kind,
                label: plainCcsLane.label,
                account_name: plainCcsLane.accountName ?? null,
                profile_name: plainCcsLane.profileName ?? null,
                project_count: plainCcsLane.projectCount,
            },
        });
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
/**
 * POST /api/accounts/default - Set default account
 */
router.post('/default', (req, res) => {
    try {
        const registry = createProfileRegistry();
        const { name } = req.body;
        if (!name) {
            res.status(400).json({ error: 'Missing required field: name' });
            return;
        }
        // Check if this is a CLIProxy account (format: "provider:accountId")
        const cliproxyKey = !hasAuthAccount(name) ? (0, account_route_helpers_1.parseCliproxyKey)(name) : null;
        if (cliproxyKey) {
            const success = (0, account_manager_1.setDefaultAccount)(cliproxyKey.provider, cliproxyKey.accountId);
            if (!success) {
                res.status(404).json({ error: `CLIProxy account not found: ${name}` });
                return;
            }
            res.json({ default: name });
            return;
        }
        // Use unified config if in unified mode, otherwise use legacy
        if ((0, config_loader_facade_1.isUnifiedMode)()) {
            registry.setDefaultUnified(name);
        }
        else {
            registry.setDefaultProfile(name);
        }
        res.json({ default: name });
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
/**
 * PUT /api/accounts/:name/context - Update account context mode/group
 */
router.put('/:name/context', async (req, res) => {
    try {
        const registry = createProfileRegistry();
        const instanceMgr = createInstanceManager();
        const { name } = req.params;
        if (!name) {
            res.status(400).json({ error: 'Missing account name' });
            return;
        }
        // CLIProxy OAuth accounts do not support local account context metadata.
        const cliproxyKey = !hasAuthAccount(name) ? (0, account_route_helpers_1.parseCliproxyKey)(name) : null;
        if (cliproxyKey) {
            res
                .status(400)
                .json({ error: `Context mode is not supported for CLIProxy account: ${name}` });
            return;
        }
        const existsUnified = (0, config_loader_facade_1.isUnifiedMode)() && registry.hasAccountUnified(name);
        const existsLegacy = registry.hasProfile(name);
        if (!existsUnified && !existsLegacy) {
            res.status(404).json({ error: `Account not found: ${name}` });
            return;
        }
        const mode = req.body?.context_mode;
        const rawGroup = req.body?.context_group;
        const rawContinuityMode = req.body?.continuity_mode;
        if (mode !== 'isolated' && mode !== 'shared') {
            res.status(400).json({ error: 'Missing or invalid context_mode: expected isolated|shared' });
            return;
        }
        if (mode !== 'shared' && rawGroup !== undefined) {
            res
                .status(400)
                .json({ error: 'Invalid payload: context_group requires context_mode=shared' });
            return;
        }
        if (mode !== 'shared' && rawContinuityMode !== undefined) {
            res
                .status(400)
                .json({ error: 'Invalid payload: continuity_mode requires context_mode=shared' });
            return;
        }
        let normalizedGroup;
        let continuityMode;
        if (mode === 'shared') {
            if (typeof rawGroup !== 'string' || rawGroup.trim().length === 0) {
                res
                    .status(400)
                    .json({ error: 'Invalid payload: shared context_mode requires non-empty context_group' });
                return;
            }
            normalizedGroup = (0, account_context_1.normalizeContextGroupName)(rawGroup);
            if (!(0, account_context_1.isValidContextGroupName)(normalizedGroup)) {
                res.status(400).json({
                    error: 'Invalid context_group. Use letters/numbers/dash/underscore, start with a letter, max 64 chars.',
                });
                return;
            }
            if (rawContinuityMode !== undefined &&
                rawContinuityMode !== 'standard' &&
                rawContinuityMode !== 'deeper') {
                res.status(400).json({
                    error: 'Invalid continuity_mode: expected standard|deeper',
                });
                return;
            }
            continuityMode = rawContinuityMode === 'deeper' ? 'deeper' : account_context_1.DEFAULT_ACCOUNT_CONTINUITY_MODE;
        }
        const metadata = mode === 'shared'
            ? {
                context_mode: 'shared',
                context_group: normalizedGroup,
                continuity_mode: continuityMode,
            }
            : {
                context_mode: 'isolated',
            };
        const policy = (0, account_context_1.resolveAccountContextPolicy)(metadata);
        const previousUnified = existsUnified ? registry.getAllAccountsUnified()[name] : undefined;
        const previousLegacy = existsLegacy ? registry.getProfile(name) : undefined;
        const resourcePolicy = (0, shared_resource_policy_1.resolveSharedResourcePolicy)(previousUnified ?? previousLegacy);
        try {
            if (existsUnified) {
                registry.updateAccountUnified(name, metadata);
            }
            if (existsLegacy) {
                registry.updateProfile(name, metadata);
            }
            await instanceMgr.ensureInstance(name, policy, { bare: resourcePolicy.profileLocal });
        }
        catch (error) {
            if (existsUnified && previousUnified) {
                registry.updateAccountUnified(name, {
                    ...previousUnified,
                    shared_resource_mode: previousUnified.shared_resource_mode,
                    bare: previousUnified.bare,
                });
            }
            if (existsLegacy && previousLegacy) {
                registry.updateProfile(name, {
                    ...previousLegacy,
                    shared_resource_mode: previousLegacy.shared_resource_mode,
                    bare: previousLegacy.bare,
                });
            }
            throw error;
        }
        res.json({
            name,
            context_mode: policy.mode,
            context_group: policy.group ?? null,
            continuity_mode: policy.mode === 'shared'
                ? (policy.continuityMode ?? account_context_1.DEFAULT_ACCOUNT_CONTINUITY_MODE)
                : null,
            context_inferred: false,
            continuity_inferred: false,
        });
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
/**
 * PUT /api/accounts/:name/shared-resources - Update account shared resource mode
 */
router.put('/:name/shared-resources', async (req, res) => {
    try {
        const registry = createProfileRegistry();
        const instanceMgr = createInstanceManager();
        const { name } = req.params;
        if (!name) {
            res.status(400).json({ error: 'Missing account name' });
            return;
        }
        const cliproxyKey = !hasAuthAccount(name) ? (0, account_route_helpers_1.parseCliproxyKey)(name) : null;
        if (cliproxyKey) {
            res.status(400).json({
                error: `Shared resource mode is not supported for CLIProxy account: ${name}`,
            });
            return;
        }
        const existsUnified = (0, config_loader_facade_1.isUnifiedMode)() && registry.hasAccountUnified(name);
        const existsLegacy = registry.hasProfile(name);
        if (!existsUnified && !existsLegacy) {
            res.status(404).json({ error: `Account not found: ${name}` });
            return;
        }
        const mode = req.body?.shared_resource_mode;
        if (!(0, shared_resource_policy_1.isSharedResourceMode)(mode)) {
            res.status(400).json({
                error: 'Missing or invalid shared_resource_mode: expected shared|profile-local',
            });
            return;
        }
        const previousUnified = existsUnified ? registry.getAllAccountsUnified()[name] : undefined;
        const previousLegacy = existsLegacy ? registry.getProfile(name) : undefined;
        const previousMetadata = previousUnified ?? previousLegacy;
        const contextPolicy = (0, account_context_1.resolveAccountContextPolicy)(previousMetadata);
        const metadata = (0, shared_resource_policy_1.sharedResourceModeToMetadata)(mode);
        try {
            if (existsUnified) {
                registry.updateAccountUnified(name, metadata);
            }
            if (existsLegacy) {
                registry.updateProfile(name, metadata);
            }
            await instanceMgr.ensureInstance(name, contextPolicy, {
                bare: mode === 'profile-local',
            });
        }
        catch (error) {
            if (existsUnified && previousUnified) {
                registry.updateAccountUnified(name, {
                    ...previousUnified,
                    shared_resource_mode: previousUnified.shared_resource_mode,
                    bare: previousUnified.bare,
                });
            }
            if (existsLegacy && previousLegacy) {
                registry.updateProfile(name, {
                    ...previousLegacy,
                    shared_resource_mode: previousLegacy.shared_resource_mode,
                    bare: previousLegacy.bare,
                });
            }
            throw error;
        }
        res.json({
            name,
            shared_resource_mode: mode,
            shared_resource_inferred: false,
            bare: mode === 'profile-local' ? true : undefined,
        });
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
/**
 * DELETE /api/accounts/reset-default - Reset to CCS default
 */
router.delete('/reset-default', (_req, res) => {
    try {
        const registry = createProfileRegistry();
        if ((0, config_loader_facade_1.isUnifiedMode)()) {
            registry.clearDefaultUnified();
        }
        else {
            registry.clearDefaultProfile();
        }
        res.json({ success: true, default: null });
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
/**
 * DELETE /api/accounts/:name - Delete an account
 */
router.delete('/:name', async (req, res) => {
    try {
        const registry = createProfileRegistry();
        const instanceMgr = createInstanceManager();
        const { name } = req.params;
        if (!name) {
            res.status(400).json({ error: 'Missing account name' });
            return;
        }
        // Check if trying to delete default (for non-CLIProxy accounts)
        const currentDefault = registry.getDefaultUnified() ?? registry.getDefaultProfile();
        if (name === currentDefault) {
            res
                .status(400)
                .json({ error: 'Cannot delete the default account. Set a different default first.' });
            return;
        }
        // Check if this is a CLIProxy account (format: "provider:accountId")
        const cliproxyKey = !hasAuthAccount(name) ? (0, account_route_helpers_1.parseCliproxyKey)(name) : null;
        if (cliproxyKey) {
            const defaultCliproxyAccount = (0, account_manager_1.getDefaultAccount)(cliproxyKey.provider);
            if (defaultCliproxyAccount?.id === cliproxyKey.accountId) {
                res.status(400).json({
                    error: `Cannot delete default CLIProxy account: ${name}. Set another default first.`,
                });
                return;
            }
            const success = (0, account_manager_1.removeAccount)(cliproxyKey.provider, cliproxyKey.accountId);
            if (!success) {
                res.status(404).json({ error: `CLIProxy account not found: ${name}` });
                return;
            }
            res.json({ success: true, deleted: name });
            return;
        }
        const existsUnified = (0, config_loader_facade_1.isUnifiedMode)() && registry.hasAccountUnified(name);
        const existsLegacy = registry.hasProfile(name);
        if (!existsUnified && !existsLegacy) {
            res.status(404).json({ error: `Account not found: ${name}` });
            return;
        }
        // Match CLI remove ordering: delete instance first, metadata second.
        await instanceMgr.deleteInstance(name);
        if (existsUnified) {
            registry.removeAccountUnified(name);
        }
        if (existsLegacy) {
            registry.deleteProfile(name);
        }
        res.json({ success: true, deleted: name });
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
/**
 * POST /api/accounts/bulk-pause - Bulk pause multiple accounts
 */
router.post('/bulk-pause', (req, res) => {
    try {
        const { provider, accountIds } = req.body;
        if (!provider || !Array.isArray(accountIds)) {
            res.status(400).json({ error: 'Missing required fields: provider and accountIds (array)' });
            return;
        }
        if (!(0, provider_capabilities_1.isCLIProxyProvider)(provider)) {
            res.status(400).json({ error: `Invalid provider: ${provider}` });
            return;
        }
        // Allow empty arrays - return early success
        if (accountIds.length === 0) {
            res.json({ succeeded: [], failed: [] });
            return;
        }
        // Validate accountIds are non-empty strings
        const invalidIds = accountIds.filter((id) => typeof id !== 'string' || id.trim().length === 0);
        if (invalidIds.length > 0) {
            res.status(400).json({ error: 'Invalid accountIds: must be non-empty strings' });
            return;
        }
        const result = (0, account_manager_1.bulkPauseAccounts)(provider, accountIds);
        res.json(result);
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
/**
 * POST /api/accounts/bulk-resume - Bulk resume multiple accounts
 */
router.post('/bulk-resume', (req, res) => {
    try {
        const { provider, accountIds } = req.body;
        if (!provider || !Array.isArray(accountIds)) {
            res.status(400).json({ error: 'Missing required fields: provider and accountIds (array)' });
            return;
        }
        if (!(0, provider_capabilities_1.isCLIProxyProvider)(provider)) {
            res.status(400).json({ error: `Invalid provider: ${provider}` });
            return;
        }
        // Allow empty arrays - return early success
        if (accountIds.length === 0) {
            res.json({ succeeded: [], failed: [] });
            return;
        }
        // Validate accountIds are non-empty strings
        const invalidIds = accountIds.filter((id) => typeof id !== 'string' || id.trim().length === 0);
        if (invalidIds.length > 0) {
            res.status(400).json({ error: 'Invalid accountIds: must be non-empty strings' });
            return;
        }
        const result = (0, account_manager_1.bulkResumeAccounts)(provider, accountIds);
        res.json(result);
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
/**
 * POST /api/accounts/solo - Solo mode: activate one account, pause all others
 */
router.post('/solo', async (req, res) => {
    try {
        const { provider, accountId } = req.body;
        if (!provider || !accountId) {
            res.status(400).json({ error: 'Missing required fields: provider and accountId' });
            return;
        }
        if (!(0, provider_capabilities_1.isCLIProxyProvider)(provider)) {
            res.status(400).json({ error: `Invalid provider: ${provider}` });
            return;
        }
        const result = await (0, account_manager_1.soloAccount)(provider, accountId);
        if (!result) {
            res.status(404).json({ error: `Account not found: ${accountId}` });
            return;
        }
        res.json(result);
    }
    catch (error) {
        res.status(500).json({ error: error.message });
    }
});
exports.default = router;
//# sourceMappingURL=account-routes.js.map