import { getDatabase } from '../database/init.js';
import { getCloudConfig, getCloudSyncControls, getDeviceId, getDeviceName, isSensitiveLevel, shouldSyncProject, updateLastSyncAt, } from './config.js';
import { enqueueFailedMemorySync } from './sync-queue.js';
function safeJsonParse(value, fallback) {
    if (!value)
        return fallback;
    try {
        return JSON.parse(value);
    }
    catch {
        return fallback;
    }
}
function rowToSyncRecord(row) {
    return {
        external_id: row.uuid,
        local_id: row.id,
        type: row.type,
        category: row.category,
        title: row.title,
        content: row.content,
        project: row.project ?? null,
        tags: safeJsonParse(row.tags, []),
        salience: Number(row.salience ?? 0),
        scope: row.scope ?? 'project',
        transferable: Boolean(row.transferable),
        trust_score: row.trust_score === undefined ? null : Number(row.trust_score),
        sensitivity_level: row.sensitivity_level ?? null,
        source: row.source ?? null,
        metadata: safeJsonParse(row.metadata, {}),
        cloud_excluded: Boolean(row.cloud_excluded),
        created_at: new Date(row.created_at ?? Date.now()).toISOString(),
        updated_at: new Date(row.updated_at ?? row.created_at ?? Date.now()).toISOString(),
        deleted_at: null,
    };
}
function buildEnvelope(records) {
    return {
        memories: records,
        device: {
            device_id: getDeviceId(),
            device_name: getDeviceName(),
            platform: `${process.platform}/${process.arch}`,
        },
    };
}
function shouldSyncRecord(record) {
    if (record.cloud_excluded)
        return false;
    const controls = getCloudSyncControls();
    if (!shouldSyncProject(record.project, controls))
        return false;
    if (controls.excludeSensitive && isSensitiveLevel(record.sensitivity_level))
        return false;
    return true;
}
function applyContentMode(record) {
    const controls = getCloudSyncControls();
    if (controls.contentMode !== 'metadata')
        return record;
    return {
        ...record,
        title: `[Metadata only] ${record.category}`,
        content: `[ShieldCortex] Memory content redacted by local sync policy.`,
        metadata: {
            ...record.metadata,
            _shieldcortex_sync: {
                ...(typeof record.metadata?._shieldcortex_sync === 'object' && record.metadata._shieldcortex_sync !== null
                    ? record.metadata._shieldcortex_sync
                    : {}),
                content_redacted: true,
                mode: 'metadata',
            },
        },
    };
}
function hydrateMemoryRecord(memoryId) {
    const db = getDatabase();
    const row = db.prepare('SELECT * FROM memories WHERE id = ?').get(memoryId);
    if (!row)
        return null;
    return rowToSyncRecord(row);
}
async function postEnvelope(envelope) {
    const config = getCloudConfig();
    if (!config.cloudEnabled || !config.cloudApiKey || envelope.memories.length === 0) {
        return false;
    }
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 15_000);
    try {
        const res = await fetch(`${config.cloudBaseUrl}/v1/sync/memories`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                Authorization: `Bearer ${config.cloudApiKey}`,
            },
            body: JSON.stringify(envelope),
            signal: controller.signal,
        });
        if (!res.ok) {
            return false;
        }
        updateLastSyncAt();
        return true;
    }
    catch {
        return false;
    }
    finally {
        clearTimeout(timeoutId);
    }
}
export function syncMemoryUpsertToCloud(memory) {
    const config = getCloudConfig();
    if (!config.cloudEnabled || !config.cloudApiKey)
        return;
    const record = hydrateMemoryRecord(memory.id);
    if (!record)
        return;
    if (!shouldSyncRecord(record))
        return;
    const envelope = buildEnvelope([applyContentMode(record)]);
    postEnvelope(envelope).then((ok) => {
        if (!ok) {
            enqueueFailedMemorySync(envelope.memories[0]);
        }
    }).catch(() => {
        enqueueFailedMemorySync(envelope.memories[0]);
    });
}
export function syncMemoryDeleteToCloud(memory) {
    const config = getCloudConfig();
    if (!config.cloudEnabled || !config.cloudApiKey)
        return;
    const record = {
        external_id: memory.uuid,
        local_id: memory.id,
        type: memory.type,
        category: memory.category,
        title: memory.title,
        content: memory.content,
        project: memory.project ?? null,
        tags: memory.tags,
        salience: memory.salience,
        scope: memory.scope,
        transferable: memory.transferable,
        trust_score: null,
        sensitivity_level: null,
        source: null,
        metadata: memory.metadata,
        created_at: memory.createdAt.toISOString(),
        updated_at: memory.updatedAt.toISOString(),
        deleted_at: new Date().toISOString(),
    };
    const envelope = buildEnvelope([record]);
    postEnvelope(envelope).then((ok) => {
        if (!ok) {
            enqueueFailedMemorySync(record);
        }
    }).catch(() => {
        enqueueFailedMemorySync(record);
    });
}
export async function syncAllMemoriesToCloud() {
    const db = getDatabase();
    const rows = db.prepare('SELECT * FROM memories ORDER BY id ASC').all();
    const records = rows.map(rowToSyncRecord).filter(shouldSyncRecord).map(applyContentMode);
    let synced = 0;
    let failed = 0;
    const batchSize = 50;
    for (let i = 0; i < records.length; i += batchSize) {
        const batch = records.slice(i, i + batchSize);
        const ok = await postEnvelope(buildEnvelope(batch));
        if (ok) {
            synced += batch.length;
        }
        else {
            failed += batch.length;
            for (const record of batch) {
                enqueueFailedMemorySync(record);
            }
        }
    }
    return { total: records.length, synced, failed };
}
