import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
import { Repository } from '../../db/repository.js';
import { executeTool, setDashboardRepo, TOOL_DEFS } from './dashboard.js';
import type { ToolContext } from './core.js';

function ctx(ownerId: string | null): ToolContext & { ownerId: string | null } {
  return {
    workspacePath: '/tmp/dummy',
    editAllowed: true,
    ownerId,
  };
}

describe('UpdateDashboardWidget tool', () => {
  let tmpDir: string;
  let repo: Repository;

  beforeEach(() => {
    tmpDir = mkdtempSync(join(tmpdir(), 'dashboard-tool-test-'));
    repo = new Repository(join(tmpDir, 'test.db'));
    setDashboardRepo(repo);
  });

  afterEach(() => {
    rmSync(tmpDir, { recursive: true, force: true });
    setDashboardRepo(null);
  });

  it('exposes a TOOL_DEFS entry', () => {
    expect(TOOL_DEFS.UpdateDashboardWidget).toBeDefined();
    expect(TOOL_DEFS.UpdateDashboardWidget!.function.name).toBe('UpdateDashboardWidget');
  });

  it('creates a new widget when slug does not exist', async () => {
    const result = await executeTool('UpdateDashboardWidget',
      { slug: 'news', title: 'News', content: 'first' },
      ctx('u1'),
    );
    expect(result?.isError).toBe(false);
    const list = await repo.listDashboardWidgets('u1');
    expect(list).toHaveLength(1);
    expect(list[0]!.slug).toBe('news');
    expect(list[0]!.markdownContent).toBe('first');
  });

  it('updates existing widget when slug exists', async () => {
    await repo.createDashboardWidget({ userId: 'u1', slug: 'news', title: 'News', content: 'old' });
    const result = await executeTool('UpdateDashboardWidget',
      { slug: 'news', content: 'new' },
      ctx('u1'),
    );
    expect(result?.isError).toBe(false);
    const list = await repo.listDashboardWidgets('u1');
    expect(list[0]!.markdownContent).toBe('new');
  });

  it('appends when mode=append', async () => {
    await repo.createDashboardWidget({ userId: 'u1', slug: 'log', title: 'L', content: 'a' });
    await executeTool('UpdateDashboardWidget',
      { slug: 'log', content: 'b', mode: 'append' },
      ctx('u1'),
    );
    const list = await repo.listDashboardWidgets('u1');
    expect(list[0]!.markdownContent).toBe('a\n\nb');
  });

  it('rejects new widget without title', async () => {
    const result = await executeTool('UpdateDashboardWidget',
      { slug: 'new-one', content: 'x' },
      ctx('u1'),
    );
    expect(result?.isError).toBe(true);
    expect(result?.output).toMatch(/title/i);
  });

  it('rejects invalid slug', async () => {
    const result = await executeTool('UpdateDashboardWidget',
      { slug: 'Bad Slug!', title: 't', content: 'x' },
      ctx('u1'),
    );
    expect(result?.isError).toBe(true);
    expect(result?.output).toMatch(/slug/i);
  });

  it('rejects content larger than 64KB', async () => {
    const big = 'x'.repeat(65 * 1024);
    const result = await executeTool('UpdateDashboardWidget',
      { slug: 'big', title: 'B', content: big },
      ctx('u1'),
    );
    expect(result?.isError).toBe(true);
    expect(result?.output).toMatch(/size|limit|64/i);
  });

  it('rejects when ownerId missing', async () => {
    const result = await executeTool('UpdateDashboardWidget',
      { slug: 'x', title: 'X', content: 'y' },
      ctx(null),
    );
    expect(result?.isError).toBe(true);
  });

  it('rejects when repo not injected', async () => {
    setDashboardRepo(null);
    const result = await executeTool('UpdateDashboardWidget',
      { slug: 'x', title: 'X', content: 'y' },
      ctx('u1'),
    );
    expect(result?.isError).toBe(true);
  });
});
