import {
  afterEach,
  beforeEach,
  describe,
  expect,
  it,
  vi
} from 'vitest';
import {
  cp,
  mkdtemp,
  mkdir,
  rm,
  writeFile
} from 'fs/promises';
import { join } from 'path';
import { tmpdir } from 'os';
import { TEST_PLUGIN_FIXTURE_DIR, TEST_PLUGIN_ID } from '../e2e/helpers/plugin-api-test-fixture';

const { mockGetPath } = vi.hoisted(() => ({
  mockGetPath: vi.fn()
}));

vi.mock('electron', () => ({
  app: {
    getPath: mockGetPath
  }
}));

import { getLocalPluginsPath, listLocalPluginManifests } from './plugin-library';

describe('plugin-library', () => {
  let userDataPath: string;

  beforeEach(async () => {
    userDataPath = await mkdtemp(join(tmpdir(), 'metoyou-plugin-library-'));
    mockGetPath.mockReturnValue(userDataPath);
  });

  afterEach(async () => {
    await rm(userDataPath, { recursive: true, force: true });
    mockGetPath.mockReset();
  });

  it('creates and reports the local plugins folder', async () => {
    const pluginsPath = await getLocalPluginsPath();
    const result = await listLocalPluginManifests();

    expect(pluginsPath).toBe(join(userDataPath, 'plugins'));
    expect(result).toEqual({
      errors: [],
      plugins: [],
      pluginsPath
    });
  });

  it('discovers immediate child plugin manifests and safe relative files', async () => {
    const pluginRoot = join(userDataPath, 'plugins', 'api-test-plugin');

    await cp(TEST_PLUGIN_FIXTURE_DIR, pluginRoot, { recursive: true });

    const result = await listLocalPluginManifests();

    expect(result.errors).toEqual([]);
    expect(result.plugins).toHaveLength(1);
    expect(result.plugins[0]).toEqual(expect.objectContaining({
      entrypointPath: join(pluginRoot, 'dist', 'main.js'),
      manifestPath: join(pluginRoot, 'toju-plugin.json'),
      pluginRoot,
      readmePath: join(pluginRoot, 'README.md')
    }));

    expect(result.plugins[0]?.manifest).toEqual(expect.objectContaining({ id: TEST_PLUGIN_ID }));
  });

  it('reports invalid JSON and keeps scanning other plugins', async () => {
    const invalidRoot = join(userDataPath, 'plugins', 'invalid-plugin');
    const validRoot = join(userDataPath, 'plugins', 'valid-plugin');

    await mkdir(invalidRoot, { recursive: true });
    await mkdir(validRoot, { recursive: true });
    await writeFile(join(invalidRoot, 'plugin.json'), '{', 'utf8');
    await writeFile(join(validRoot, 'plugin.json'), JSON.stringify({
      apiVersion: '1.0.0',
      compatibility: { minimumTojuVersion: '1.0.0' },
      description: 'Valid plugin',
      entrypoint: './main.js',
      id: 'valid.plugin',
      kind: 'client',
      schemaVersion: 1,
      title: 'Valid Plugin',
      version: '1.0.0'
    }), 'utf8');

    const result = await listLocalPluginManifests();

    expect(result.plugins.map((plugin) => plugin.pluginRoot)).toEqual([validRoot]);
    expect(result.errors).toHaveLength(1);
    expect(result.errors[0]).toEqual(expect.objectContaining({
      manifestPath: join(invalidRoot, 'plugin.json'),
      pluginRoot: invalidRoot
    }));
  });

  it('does not resolve entrypoints outside the plugin folder', async () => {
    const pluginRoot = join(userDataPath, 'plugins', 'unsafe-plugin');

    await mkdir(pluginRoot, { recursive: true });
    await writeFile(join(userDataPath, 'plugins', 'outside.js'), 'export default {};', 'utf8');
    await writeFile(join(pluginRoot, 'plugin.json'), JSON.stringify({
      apiVersion: '1.0.0',
      compatibility: { minimumTojuVersion: '1.0.0' },
      description: 'Unsafe plugin',
      entrypoint: '../outside.js',
      id: 'unsafe.plugin',
      kind: 'client',
      schemaVersion: 1,
      title: 'Unsafe Plugin',
      version: '1.0.0'
    }), 'utf8');

    const result = await listLocalPluginManifests();

    expect(result.plugins[0]?.entrypointPath).toBeUndefined();
  });
});
