/**
 * Tests for registerProject CONFLICT error disambiguation.
 * Verifies that UNIQUE constraint failures on specific columns produce
 * column-specific error messages.
 */

import { describe, it, expect, vi } from 'vitest';
import { TRPCError } from '@trpc/server';
import { router, publicProcedure, createCallerFactory } from '../trpc.js';
import { projectProcedures } from './project.js';
import type { TRPCContext } from '../context.js';
import type { ProjectRepository } from '../../db/repositories/project-repository.js';

const testRouter = router({
  ...projectProcedures(publicProcedure),
});

const createCaller = createCallerFactory(testRouter);

function makeCtx(mockCreate: () => Promise<never>): TRPCContext {
  const projectRepository: ProjectRepository = {
    create: mockCreate as unknown as ProjectRepository['create'],
    findById: vi.fn().mockResolvedValue(null),
    findByName: vi.fn().mockResolvedValue(null),
    findAll: vi.fn().mockResolvedValue([]),
    update: vi.fn(),
    delete: vi.fn(),
    addProjectToInitiative: vi.fn(),
    removeProjectFromInitiative: vi.fn(),
    findProjectsByInitiativeId: vi.fn().mockResolvedValue([]),
    setInitiativeProjects: vi.fn(),
  };

  return {
    eventBus: {} as TRPCContext['eventBus'],
    serverStartedAt: null,
    processCount: 0,
    projectRepository,
    // No workspaceRoot — prevents cloneProject from running
  };
}

const INPUT = { name: 'my-project', url: 'https://github.com/example/repo' };

describe('registerProject — CONFLICT error disambiguation', () => {
  it('throws CONFLICT with name-specific message on projects.name UNIQUE violation', async () => {
    const caller = createCaller(makeCtx(() => {
      throw new Error('UNIQUE constraint failed: projects.name');
    }));

    const err = await caller.registerProject(INPUT).catch(e => e);
    expect(err).toBeInstanceOf(TRPCError);
    expect(err.code).toBe('CONFLICT');
    expect(err.message).toBe('A project with this name already exists');
  });

  it('throws CONFLICT with url-specific message on projects.url UNIQUE violation', async () => {
    const caller = createCaller(makeCtx(() => {
      throw new Error('UNIQUE constraint failed: projects.url');
    }));

    const err = await caller.registerProject(INPUT).catch(e => e);
    expect(err).toBeInstanceOf(TRPCError);
    expect(err.code).toBe('CONFLICT');
    expect(err.message).toBe('A project with this URL already exists');
  });

  it('throws CONFLICT with fallback message on unknown UNIQUE constraint violation', async () => {
    const caller = createCaller(makeCtx(() => {
      throw new Error('UNIQUE constraint failed: projects.unknown_col');
    }));

    const err = await caller.registerProject(INPUT).catch(e => e);
    expect(err).toBeInstanceOf(TRPCError);
    expect(err.code).toBe('CONFLICT');
    expect(err.message).toBe('A project with this name or URL already exists');
  });

  it('rethrows non-UNIQUE errors without wrapping in a CONFLICT', async () => {
    const originalError = new Error('SQLITE_BUSY');
    const caller = createCaller(makeCtx(() => {
      throw originalError;
    }));

    const err = await caller.registerProject(INPUT).catch(e => e);
    expect(err).toBeDefined();
    // Must not be surfaced as a CONFLICT — the catch block should re-throw as-is
    expect(err).not.toMatchObject({ code: 'CONFLICT' });
    // The original error message must be preserved somewhere
    expect(err.message).toContain('SQLITE_BUSY');
  });
});
