import { describe, expect, it, vi } from 'vitest';
import { createMinioIngestTool, minioIngestInternals } from './minio-ingest.js';
import type { BackupConfig } from '../../config/schema.js';
import type { MemoryStore } from '../../memory/store.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 ingest internals', () => {
  it('accepts known text-like extensions', () => {
    expect(minioIngestInternals.isLikelyTextObject('notes/today.md')).toBe(true);
    expect(minioIngestInternals.isLikelyTextObject('logs/daemon.log')).toBe(true);
    expect(minioIngestInternals.isLikelyTextObject('manual.pdf')).toBe(true);
    expect(minioIngestInternals.isLikelyTextObject('manual.docx')).toBe(true);
  });

  it('rejects likely binary extensions', () => {
    expect(minioIngestInternals.isLikelyTextObject('manual.exe')).toBe(false);
  });
});

describe('createMinioIngestTool', () => {
  it('ingests object and writes to memory', async () => {
    const write = vi.fn();
    const store = { write } as unknown as MemoryStore;
    const execRunner = vi.fn(async () => ({
      stdout: '# Runbook\n\nRestart service before deploy.\n',
      stderr: '',
    }));

    const tool = createMinioIngestTool(makeBackupConfig({
      minio: {
        enabled: true,
        endpoint: 'localhost:9000',
        access_key: 'minio-admin',
        secret_key: 'minio-secret',
        bucket: 'flynn-knowledge',
        prefix: 'flynn',
        secure: false,
        mc_path: '/opt/bin/mc',
      },
    }), store, {
      execRunner,
      now: () => new Date('2026-02-16T15:00:00.000Z'),
    });

    const result = await tool.execute({
      object_key: 'knowledge/runbook.md',
      namespace: 'global/runbooks',
      mode: 'append',
    });

    expect(result.success).toBe(true);
    expect(result.output).toContain('Ingested MinIO object');
    expect(write).toHaveBeenCalledWith(
      'global/runbooks',
      expect.stringContaining('source: minio://flynn-knowledge/knowledge/runbook.md'),
      'append',
    );
    expect(execRunner).toHaveBeenCalledWith(
      '/opt/bin/mc',
      ['cat', 'flynningest/flynn-knowledge/knowledge/runbook.md'],
      expect.objectContaining({ env: expect.any(Object) }),
    );
  });

  it('rejects unsupported binary object unless force=true', async () => {
    const write = vi.fn();
    const store = { write } as unknown as MemoryStore;
    const execRunner = vi.fn();
    const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner });

    const result = await tool.execute({ object_key: 'knowledge/diagram.exe' });
    expect(result.success).toBe(false);
    expect(result.error).toContain('Unsupported object type');
    expect(execRunner).not.toHaveBeenCalled();
  });

  it('extracts PDF text with pdftotext', async () => {
    const write = vi.fn();
    const store = { write } as unknown as MemoryStore;
    const execRunner = vi.fn(async (_file: string, args: string[]) => {
      if (args[0] === 'cp') {
        return { stdout: '', stderr: '' };
      }
      if (_file === 'pdftotext') {
        return { stdout: 'Extracted PDF text', stderr: '' };
      }
      return { stdout: '', stderr: '' };
    });
    const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner });

    const result = await tool.execute({
      object_key: 'knowledge/diagram.pdf',
      mode: 'replace',
    });
    expect(result.success).toBe(true);
    expect(write).toHaveBeenCalledWith(
      'global/knowledge',
      expect.stringContaining('Extracted PDF text'),
      'replace',
    );
    expect(execRunner).toHaveBeenCalledWith(
      'mc',
      expect.arrayContaining(['cp', 'flynningest/flynn-knowledge/knowledge/diagram.pdf']),
      expect.objectContaining({ env: expect.any(Object) }),
    );
    expect(execRunner).toHaveBeenCalledWith(
      'pdftotext',
      expect.arrayContaining(['-q']),
      expect.any(Object),
    );
  });

  it('extracts DOCX text with pandoc', 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] === 'cp') {
        return { stdout: '', stderr: '' };
      }
      if (_file === 'pandoc') {
        return { stdout: 'Extracted DOCX text', stderr: '' };
      }
      return { stdout: '', stderr: '' };
    });
    const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner });

    const result = await tool.execute({
      object_key: 'knowledge/spec.docx',
      mode: 'replace',
    });
    expect(result.success).toBe(true);
    expect(write).toHaveBeenCalledWith(
      'global/knowledge',
      expect.stringContaining('Extracted DOCX text'),
      'replace',
    );
    expect(execRunner).toHaveBeenCalledWith(
      'pandoc',
      expect.arrayContaining(['-t', 'plain']),
      expect.any(Object),
    );
  });

  it('allows non-text extension when force=true', async () => {
    const write = vi.fn();
    const store = { write } as unknown as MemoryStore;
    const execRunner = vi.fn(async () => ({
      stdout: 'PDF text extracted upstream',
      stderr: '',
    }));
    const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner });

    const result = await tool.execute({
      object_key: 'knowledge/diagram.pdf',
      force: true,
      mode: 'replace',
    });
    expect(result.success).toBe(true);
    expect(write).toHaveBeenCalledWith(
      'global/knowledge',
      expect.stringContaining('PDF text extracted upstream'),
      'replace',
    );
  });

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

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