import { describe, expect, it, vi } from 'vitest';
import type { BackupConfig } from '../../config/schema.js';
import type { MemoryStore } from '../../memory/store.js';
import { createMinioSyncTool, minioSyncInternals } from './minio-sync.js';

function makeBackupConfig(overrides?: Partial<BackupConfig>): BackupConfig {
  return {
    enabled: true,
    schedule: undefined,
    interval: '24h',
    run_on_start: false,
    notify: undefined,
    failure_threshold: 1,
    notify_recovery: true,
    local_dir: '~/.local/share/flynn/backups',
    include_vectors: true,
    minio: {
      enabled: true,
      endpoint: 'localhost:9000',
      access_key: 'minio-admin',
      secret_key: 'minio-secret',
      bucket: 'flynn-knowledge',
      prefix: 'flynn',
      secure: false,
    },
    ...overrides,
  };
}

describe('minio sync internals', () => {
  it('parses file object keys from mc ls --json output', () => {
    const stdout = [
      '{"status":"success","type":"folder","key":"knowledge/"}',
      '{"status":"success","type":"file","key":"knowledge/runbook.md"}',
      '{"status":"success","type":"file","name":"knowledge/notes.txt"}',
    ].join('\n');

    expect(minioSyncInternals.parseListedObjectKeys(stdout)).toEqual([
      'knowledge/runbook.md',
      'knowledge/notes.txt',
    ]);
  });

  it('normalizes object paths into namespace-safe segments', () => {
    expect(minioSyncInternals.normalizeNamespaceSegment('knowledge/team runbook.v2.md')).toBe('knowledge/team_runbook_v2');
  });
});

describe('createMinioSyncTool', () => {
  it('syncs text-like objects into nested memory namespaces', async () => {
    const write = vi.fn();
    const store = { write } as unknown as MemoryStore;

    const execRunner = vi.fn(async (_file: string, args: string[]) => {
      if (args[0] === 'ls') {
        return {
          stdout: [
            '{"status":"success","type":"file","key":"knowledge/runbook.md"}',
            '{"status":"success","type":"file","key":"knowledge/ops/alerts.txt"}',
          ].join('\n'),
          stderr: '',
        };
      }
      if (args[0] === 'cat' && args[1]?.endsWith('knowledge/runbook.md')) {
        return { stdout: '# Runbook\nDeploy carefully.\n', stderr: '' };
      }
      if (args[0] === 'cat' && args[1]?.endsWith('knowledge/ops/alerts.txt')) {
        return { stdout: 'Alert routing notes.', stderr: '' };
      }
      return { stdout: '', stderr: '' };
    });

    const tool = createMinioSyncTool(makeBackupConfig(), store, {
      execRunner,
      now: () => new Date('2026-02-16T16:00:00.000Z'),
    });

    const result = await tool.execute({
      prefix: 'knowledge/',
      namespace_base: 'global/knowledge/minio',
      mode: 'replace',
      max_objects: 10,
    });

    expect(result.success).toBe(true);
    expect(result.output).toContain('Imported: 2');
    expect(write).toHaveBeenCalledTimes(2);
    expect(write).toHaveBeenNthCalledWith(
      1,
      'global/knowledge/minio/knowledge/runbook',
      expect.stringContaining('source: minio://flynn-knowledge/knowledge/runbook.md'),
      'replace',
    );
    expect(write).toHaveBeenNthCalledWith(
      2,
      'global/knowledge/minio/knowledge/ops/alerts',
      expect.stringContaining('Alert routing notes.'),
      'replace',
    );
  });

  it('skips binary-like extensions by default', async () => {
    const write = vi.fn();
    const store = { write } as unknown as MemoryStore;
    const execRunner = vi.fn(async (_file: string, args: string[]) => {
      if (args[0] === 'ls') {
        return { stdout: '{"status":"success","type":"file","key":"knowledge/diagram.exe"}', stderr: '' };
      }
      return { stdout: 'fake text', stderr: '' };
    });

    const tool = createMinioSyncTool(makeBackupConfig(), store, { execRunner });
    const result = await tool.execute({ prefix: 'knowledge/' });
    expect(result.success).toBe(true);
    expect(result.output).toContain('Imported: 0');
    expect(result.output).toContain('Skipped: 1');
    expect(write).not.toHaveBeenCalled();
  });

  it('syncs DOCX objects via text extraction', async () => {
    const write = vi.fn();
    const store = { write } as unknown as MemoryStore;
    const execRunner = vi.fn(async (_file: string, args: string[]) => {
      if (_file === 'mc' && args[0] === 'ls') {
        return { stdout: '{"status":"success","type":"file","key":"knowledge/spec.docx"}', stderr: '' };
      }
      if (_file === 'mc' && args[0] === 'cp') {
        return { stdout: '', stderr: '' };
      }
      if (_file === 'pandoc') {
        return { stdout: 'DOCX sync text', stderr: '' };
      }
      return { stdout: '', stderr: '' };
    });

    const tool = createMinioSyncTool(makeBackupConfig(), store, { execRunner });
    const result = await tool.execute({ prefix: 'knowledge/' });
    expect(result.success).toBe(true);
    expect(result.output).toContain('Imported: 1');
    expect(write).toHaveBeenCalledWith(
      'global/knowledge/minio/knowledge/spec',
      expect.stringContaining('DOCX sync text'),
      'append',
    );
  });

  it('returns an error when minio is disabled', async () => {
    const write = vi.fn();
    const store = { write } as unknown as MemoryStore;
    const tool = createMinioSyncTool(makeBackupConfig({
      minio: {
        enabled: false,
        endpoint: undefined,
        access_key: undefined,
        secret_key: undefined,
        bucket: undefined,
        prefix: 'flynn',
        secure: true,
      },
    }), store);

    const result = await tool.execute({ prefix: 'knowledge/' });
    expect(result.success).toBe(false);
    expect(result.error).toContain('backup.minio.enabled=true');
  });
});
