import Conf from 'conf';
import fs from 'node:fs';
import path from 'node:path';
import { createFallbackInstanceIdentifier, createProjectSlug, resolveInstanceIdentifier } from 'n8nac';
import { ensureYagrHomeDir, getYagrN8nWorkspaceDir, getYagrPaths } from './yagr-home.js';
/**
 * Computes the fully-qualified workflow directory for the current config:
 *   <syncFolder>/<instanceIdentifier>/<projectSlug>
 *
 * Returns undefined when any required field is missing (e.g. during bootstrap).
 * This is the single source of truth for this path calculation.
 */
export function resolveWorkflowDir(config) {
    const { syncFolder, instanceIdentifier, projectName } = config;
    if (!syncFolder || !instanceIdentifier || !projectName) {
        return undefined;
    }
    const workspaceDir = getYagrN8nWorkspaceDir();
    const resolvedSyncFolder = path.isAbsolute(syncFolder)
        ? syncFolder
        : path.join(workspaceDir, syncFolder);
    // Strip characters that are invalid in Windows path components (colon, etc.)
    // Identifiers stored on Linux/macOS may contain ':' from IP:port slugs.
    const safeInstanceId = instanceIdentifier.replace(/[:<>"|?*]/g, '_');
    return path.join(resolvedSyncFolder, safeInstanceId, createProjectSlug(projectName));
}
function sanitizeRuntimeValue(value) {
    const trimmed = String(value ?? '').trim().replace(/^['"]|['"]$/g, '');
    return trimmed || undefined;
}
export function resolveN8nRuntimeState(configService, env = process.env, options = {}) {
    const localConfig = configService.getLocalConfig();
    const envHost = options.allowEnvironmentFallback ? sanitizeRuntimeValue(env.N8N_HOST) : undefined;
    const host = sanitizeRuntimeValue(localConfig.host) ?? envHost;
    const storedApiKey = host ? sanitizeRuntimeValue(configService.getApiKey(host)) : undefined;
    const envApiKey = options.allowEnvironmentFallback ? sanitizeRuntimeValue(env.N8N_API_KEY) : undefined;
    const apiKey = storedApiKey ?? envApiKey;
    const projectConfigured = Boolean(host
        && localConfig.syncFolder
        && localConfig.projectId
        && localConfig.projectName);
    return {
        host,
        apiKey,
        syncFolder: localConfig.syncFolder,
        projectId: localConfig.projectId,
        projectName: localConfig.projectName,
        instanceIdentifier: localConfig.instanceIdentifier,
        workflowDir: resolveWorkflowDir(localConfig),
        credentialsAvailable: Boolean(host && apiKey),
        projectConfigured,
        initialized: Boolean(projectConfigured && apiKey),
    };
}
export class YagrN8nConfigService {
    globalStore;
    compatibilityStore;
    localConfigPath;
    legacyCredentialsPath;
    constructor() {
        const paths = getYagrPaths();
        ensureYagrHomeDir();
        this.globalStore = new Conf({
            cwd: paths.homeDir,
            configName: 'n8n-credentials',
        });
        this.compatibilityStore = new Conf({
            projectName: 'n8nac',
            configName: 'credentials',
        });
        this.localConfigPath = paths.n8nConfigPath;
        this.legacyCredentialsPath = paths.legacyN8nCredentialsPath;
        this.migrateLegacyCredentials();
        this.syncCompatibilityCredentials();
    }
    getLocalConfig() {
        if (!fs.existsSync(this.localConfigPath)) {
            return {};
        }
        try {
            const content = fs.readFileSync(this.localConfigPath, 'utf-8');
            return JSON.parse(content);
        }
        catch {
            return {};
        }
    }
    saveLocalConfig(config) {
        fs.writeFileSync(this.localConfigPath, JSON.stringify(config, null, 2));
    }
    saveBootstrapState(host, syncFolder = 'workflows', runtimeSource = 'external') {
        const current = this.getLocalConfig();
        const bootstrapState = {
            host,
            syncFolder,
            runtimeSource,
        };
        if (current.customNodesPath) {
            bootstrapState.customNodesPath = current.customNodesPath;
        }
        this.saveLocalConfig(bootstrapState);
    }
    getApiKey(host) {
        const credentials = this.globalStore.get('hosts') ?? {};
        const normalizedHost = this.normalizeHost(host);
        if (credentials[normalizedHost]) {
            return credentials[normalizedHost];
        }
        const compatibilityCredentials = this.compatibilityStore.get('hosts') ?? {};
        return compatibilityCredentials[normalizedHost];
    }
    saveApiKey(host, apiKey) {
        const credentials = this.globalStore.get('hosts') ?? {};
        const compatibilityCredentials = this.compatibilityStore.get('hosts') ?? {};
        const normalizedHost = this.normalizeHost(host);
        credentials[normalizedHost] = apiKey;
        compatibilityCredentials[normalizedHost] = apiKey;
        this.globalStore.set('hosts', credentials);
        this.compatibilityStore.set('hosts', compatibilityCredentials);
    }
    clearLocalConfig() {
        if (fs.existsSync(this.localConfigPath)) {
            fs.unlinkSync(this.localConfigPath);
        }
    }
    clearAllApiKeys() {
        this.globalStore.set('hosts', {});
        this.compatibilityStore.set('hosts', {});
    }
    async getOrCreateInstanceIdentifier(host) {
        const local = this.getLocalConfig();
        const apiKey = this.getApiKey(host);
        if (!apiKey) {
            throw new Error('API key not found');
        }
        try {
            const { identifier } = await resolveInstanceIdentifier({ host, apiKey });
            this.saveLocalConfig({
                ...local,
                host,
                instanceIdentifier: identifier,
            });
            return identifier;
        }
        catch {
            const fallbackIdentifier = createFallbackInstanceIdentifier(host, apiKey);
            this.saveLocalConfig({
                ...local,
                host,
                instanceIdentifier: fallbackIdentifier,
            });
            return fallbackIdentifier;
        }
    }
    normalizeHost(host) {
        try {
            const url = new URL(host);
            return url.origin;
        }
        catch {
            return host.replace(/\/$/, '');
        }
    }
    migrateLegacyCredentials() {
        if (this.globalStore.path === this.legacyCredentialsPath) {
            return;
        }
        const currentHosts = this.globalStore.get('hosts') ?? {};
        if (Object.keys(currentHosts).length > 0 || !fs.existsSync(this.legacyCredentialsPath)) {
            return;
        }
        try {
            const content = JSON.parse(fs.readFileSync(this.legacyCredentialsPath, 'utf-8'));
            const legacyHosts = content.hosts ?? {};
            if (Object.keys(legacyHosts).length > 0) {
                this.globalStore.set('hosts', legacyHosts);
            }
        }
        catch {
            // Ignore malformed legacy files and start fresh in the Yagr home.
        }
    }
    syncCompatibilityCredentials() {
        const homeHosts = this.globalStore.get('hosts') ?? {};
        const compatibilityHosts = this.compatibilityStore.get('hosts') ?? {};
        const mergedHosts = {
            ...compatibilityHosts,
            ...homeHosts,
        };
        if (Object.keys(mergedHosts).length === 0) {
            return;
        }
        this.globalStore.set('hosts', mergedHosts);
        this.compatibilityStore.set('hosts', mergedHosts);
    }
}
//# sourceMappingURL=n8n-config-service.js.map