import { describe, it, expect, beforeAll } from 'vitest';
import { sql } from 'drizzle-orm';
import { createPgliteClient } from '@platform-modules/db/pglite';
import {
  contentSchema,
  publish,
  put,
  type ContentSchema,
} from '@platform-modules/content';
import {
  fieldsMigrationSql,
  fieldsSchema,
  setEntityValues,
  type FieldsSchema,
} from '@platform-modules/fields';
import type { TransactionalDatabase } from '@platform-modules/db';
import { DEFAULT_CONTENT_FIELD_GROUP } from '../../lib/fields.js';
import { sanitizeContentBody } from '../../lib/sanitize.js';

function escapeHtml(value: string): string {
  return value
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;');
}

/** Mirrors the public post `<dl>` render contract in `post/[slug].astro` — the `<dl>` is rendered in the theme PostBody (DefaultPostBody/EditorialPostBody); field loading stays in post/[slug].astro. */
export function renderContentAttrsDl(values: Record<string, unknown>): string {
  const chunks: string[] = ['<dl class="content-fields">'];
  for (const field of DEFAULT_CONTENT_FIELD_GROUP.fields) {
    const value = values[field.key];
    if (value === undefined || value === null || value === '') continue;
    chunks.push(`<dt>${field.label}</dt>`);
    if (field.type === 'media') {
      const media = value as { url?: string };
      if (media.url) {
        chunks.push(
          `<dd>${sanitizeContentBody(`<img src="${media.url}" alt="" />`)}</dd>`,
        );
      }
    } else {
      chunks.push(`<dd>${escapeHtml(String(value))}</dd>`);
    }
  }
  chunks.push('</dl>');
  return chunks.join('');
}

type Combined = ContentSchema & FieldsSchema;

describe('post page — public field values render', () => {
  let values: Record<string, unknown>;

  beforeAll(async () => {
    const db = createPgliteClient({ schema: { ...contentSchema, ...fieldsSchema } });
    await db.execute(sql`
      CREATE TABLE content_entries (
        id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
        slug text NOT NULL,
        type text NOT NULL,
        title text NOT NULL,
        body text NOT NULL DEFAULT '',
        status text NOT NULL DEFAULT 'draft',
        visibility text NOT NULL DEFAULT 'public',
        published_at timestamptz(3),
        author text NOT NULL,
        category jsonb NOT NULL DEFAULT '[]'::jsonb,
        tag jsonb NOT NULL DEFAULT '[]'::jsonb,
        created_at timestamptz(3) NOT NULL DEFAULT NOW(),
        updated_at timestamptz(3) NOT NULL DEFAULT NOW()
      )
    `);
    await db.execute(sql`
      CREATE UNIQUE INDEX content_entries_type_slug_uq ON content_entries (type, slug)
    `);
    await db.execute(sql`
      CREATE TABLE content_terms (
        id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
        taxonomy text NOT NULL,
        slug text NOT NULL,
        name text NOT NULL,
        parent_id uuid REFERENCES content_terms(id) ON DELETE RESTRICT,
        depth integer NOT NULL DEFAULT 0,
        created_at timestamptz(3) NOT NULL DEFAULT NOW()
      )
    `);
    await db.execute(sql`
      CREATE TABLE content_entry_terms (
        entry_id uuid NOT NULL REFERENCES content_entries(id) ON DELETE CASCADE,
        term_id uuid NOT NULL REFERENCES content_terms(id) ON DELETE CASCADE,
        PRIMARY KEY (entry_id, term_id)
      )
    `);
    for (const stmt of fieldsMigrationSql()
      .split(';')
      .map((s) => s.trim())
      .filter(Boolean)) {
      await db.execute(sql.raw(stmt));
    }

    const actor = { id: 'author', canEditAny: true, canPublish: true };
    const entry = await put(
      db,
      { slug: 'fields-render', type: 'post', title: 'Fields', body: 'b' },
      actor,
      (raw: string) => raw,
    );
    await publish(db, entry.id, actor);

    const resolved = {
      ...DEFAULT_CONTENT_FIELD_GROUP,
      origin: 'code' as const,
    };
    await setEntityValues(db as TransactionalDatabase<FieldsSchema>, { id: 'author', canEditFields: true }, {
      ref: { entityType: 'content', entityId: entry.id },
      groupId: 'content_attrs',
      values: {
        subtitle: '<script>alert(1)</script>',
        featured_image: { key: 'img-1', url: 'https://cdn.example/hero.jpg' },
        seo_description: 'About this post',
      },
      resolved,
    });

    const { getEntityValues } = await import('@platform-modules/fields');
    values = await getEntityValues(
      db as TransactionalDatabase<FieldsSchema>,
      { entityType: 'content', entityId: entry.id },
      { entityType: 'content', subType: 'post', codeGroups: [DEFAULT_CONTENT_FIELD_GROUP] },
    );
  });

  it('renders content_attrs values escaped; media only via sanitizeContentBody img allowlist', () => {
    const html = renderContentAttrsDl(values);
    expect(html).toContain('&lt;script&gt;alert(1)&lt;/script&gt;');
    expect(html).not.toMatch(/<script>alert\(1\)<\/script>/);
    expect(html).toContain('<img src="https://cdn.example/hero.jpg"');
    expect(html).not.toContain('javascript:');
    expect(html).toContain('About this post');
  });
});
