import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import { PrismaClient } from '@prisma/client';
import { resetDb } from '../test-utils/reset-db';
import { TemplateNotFoundError, TemplateRepository } from './template.repository';
import type { PrismaService } from '../prisma/prisma.service';

const url = process.env.DATABASE_URL ?? 'postgresql://iios:iios@localhost:5434/iios?schema=public';
const prisma = new PrismaClient({ datasources: { db: { url } } });
const repo = new TemplateRepository(prisma as unknown as PrismaService);

async function scope(): Promise<string> {
  const s = await prisma.iiosScope.create({ data: { orgId: 'org_demo', appId: 'crm-web' } });
  return s.id;
}
async function seed(data: Partial<Parameters<typeof prisma.iiosMessageTemplate.create>[0]['data']> & { key: string }) {
  return prisma.iiosMessageTemplate.create({
    data: { channel: 'EMAIL', locale: 'en', version: 1, bodyText: 't', ...data } as never,
  });
}

beforeAll(async () => { await prisma.$connect(); });
afterAll(async () => { await prisma.$disconnect(); });
beforeEach(async () => { await resetDb(prisma); });

describe('TemplateRepository.resolve', () => {
  it('returns the global default when no scope override exists', async () => {
    await seed({ key: 'welcome', bodyText: 'global' });
    const t = await repo.resolve('welcome', 'EMAIL', 'en', await scope());
    expect(t.bodyText).toBe('global');
    expect(t.scopeId).toBeNull();
  });

  it('prefers the scoped override over the global default', async () => {
    const scopeId = await scope();
    await seed({ key: 'welcome', bodyText: 'global' });                 // scopeId NULL
    await seed({ key: 'welcome', bodyText: 'scoped', scopeId });        // override
    const t = await repo.resolve('welcome', 'EMAIL', 'en', scopeId);
    expect(t.bodyText).toBe('scoped');
    expect(t.scopeId).toBe(scopeId);
  });

  it('returns the highest active version', async () => {
    await seed({ key: 'welcome', version: 1, bodyText: 'v1' });
    await seed({ key: 'welcome', version: 3, bodyText: 'v3' });
    await seed({ key: 'welcome', version: 2, bodyText: 'v2' });
    const t = await repo.resolve('welcome', 'EMAIL', 'en');
    expect(t.version).toBe(3);
  });

  it('ignores inactive versions (a retracted v3 falls back to v2)', async () => {
    await seed({ key: 'welcome', version: 2, bodyText: 'v2' });
    await seed({ key: 'welcome', version: 3, bodyText: 'v3', active: false });
    const t = await repo.resolve('welcome', 'EMAIL', 'en');
    expect(t.version).toBe(2);
  });

  it('throws TemplateNotFoundError for an unknown key', async () => {
    await expect(repo.resolve('nope', 'EMAIL', 'en')).rejects.toBeInstanceOf(TemplateNotFoundError);
  });

  it('does not cross channels (an EMAIL template is not an SMS template)', async () => {
    await seed({ key: 'welcome', channel: 'EMAIL' });
    await expect(repo.resolve('welcome', 'SMS', 'en')).rejects.toBeInstanceOf(TemplateNotFoundError);
  });
});
