## Task 3: Derivazione dello stato PDF

**Files:**
- Create: `app/src/lib/server/pdf/state.ts`
- Create: `app/src/lib/server/pdf/state.test.ts`

**Interfaces:**
- Consumes: `computeDvrFingerprint` (Task 2), `db` da `$lib/server/db`, `dvrPdf` schema (Task 1).
- Produces:
  - `export type PdfState = 'ready' | 'stale' | 'generating' | 'error' | 'missing'`
  - `export function derivePdfState(row: { stato: string|null; dataHash: string|null } | null, currentHash: string): PdfState`
  - `export type PdfStatus = { state: PdfState; currentHash: string; generatoIl: string|null; r2Key: string|null; errorMsg: string|null }`
  - `export async function getPdfStatus(agenziaId: number): Promise<PdfStatus>`
  - `export async function markGenerating(agenziaId: number, currentHash: string): Promise<void>`

- [ ] **Step 1: Scrivi il test della funzione pura `derivePdfState`**

Crea `app/src/lib/server/pdf/state.test.ts`:

```ts
import { describe, it, expect } from 'vitest';
import { derivePdfState } from './state';

describe('derivePdfState', () => {
	const H = 'abc';
	it('missing se non c’è riga', () => expect(derivePdfState(null, H)).toBe('missing'));
	it('generating se in corso', () => expect(derivePdfState({ stato: 'generating', dataHash: 'old' }, H)).toBe('generating'));
	it('error se errore', () => expect(derivePdfState({ stato: 'error', dataHash: 'old' }, H)).toBe('error'));
	it('ready se hash combacia', () => expect(derivePdfState({ stato: 'ready', dataHash: H }, H)).toBe('ready'));
	it('stale se ready ma hash diverso', () => expect(derivePdfState({ stato: 'ready', dataHash: 'old' }, H)).toBe('stale'));
	it('missing se ready ma senza hash', () => expect(derivePdfState({ stato: 'ready', dataHash: null }, H)).toBe('missing'));
});
```

- [ ] **Step 2: Esegui e verifica fallimento**

Run: `cd app && npx vitest run src/lib/server/pdf/state.test.ts`
Expected: FAIL (modulo inesistente).

- [ ] **Step 3: Implementa `state.ts`**

Crea `app/src/lib/server/pdf/state.ts`:

```ts
import { createClient } from '@libsql/client';
import { env } from '$env/dynamic/private';
import { db } from '$lib/server/db';
import { dvrPdf } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';
import { computeDvrFingerprint } from './fingerprint';

export type PdfState = 'ready' | 'stale' | 'generating' | 'error' | 'missing';

export function derivePdfState(
	row: { stato: string | null; dataHash: string | null } | null,
	currentHash: string
): PdfState {
	if (!row || !row.stato) return 'missing';
	if (row.stato === 'generating') return 'generating';
	if (row.stato === 'error') return 'error';
	if (row.stato === 'ready') {
		if (!row.dataHash) return 'missing';
		return row.dataHash === currentHash ? 'ready' : 'stale';
	}
	return 'missing';
}

export type PdfStatus = {
	state: PdfState;
	currentHash: string;
	generatoIl: string | null;
	r2Key: string | null;
	errorMsg: string | null;
};

function fpClient() {
	return createClient({
		url: env.DATABASE_URL ?? 'file:../import-tools/db/dev.db',
		authToken: env.DATABASE_AUTH_TOKEN
	});
}

export async function getPdfStatus(agenziaId: number): Promise<PdfStatus> {
	const client = fpClient();
	const currentHash = await computeDvrFingerprint(client, agenziaId);
	const [row] = await db.select().from(dvrPdf).where(eq(dvrPdf.agenziaId, agenziaId));
	return {
		state: derivePdfState(row ?? null, currentHash),
		currentHash,
		generatoIl: row?.generatoIl ?? null,
		r2Key: row?.r2Key ?? null,
		errorMsg: row?.errorMsg ?? null
	};
}

export async function markGenerating(agenziaId: number, currentHash: string): Promise<void> {
	const now = new Date().toISOString();
	await db
		.insert(dvrPdf)
		.values({ agenziaId, stato: 'generating', updatedAt: now })
		.onConflictDoUpdate({
			target: dvrPdf.agenziaId,
			set: { stato: 'generating', updatedAt: now, errorMsg: null }
		});
}
```

- [ ] **Step 4: Esegui e verifica passaggio**

Run: `cd app && npx vitest run src/lib/server/pdf/state.test.ts`
Expected: 6 test PASS.

- [ ] **Step 5: Type-check**

Run: `cd app && npm run check`
Expected: 0 errori.

- [ ] **Step 6: Commit**

```bash
git add app/src/lib/server/pdf/state.ts app/src/lib/server/pdf/state.test.ts
git commit -m "PDF: derivazione stato (ready/stale/generating/error/missing)"
```

---

