import { Hono } from 'hono';
import { randomUUID } from 'node:crypto';
import { db, type CustomPageRow } from '../db.js';
import type { CustomPage } from '../types.js';
import { renderMarkdown } from '../markdown.js';
import { beautify, AiNotConfiguredError, isAiConfigured } from '../ai.js';

export const pages = new Hono();

/** Convert a snake_case DB row into the camelCase API shape. */
function hydrate(row: CustomPageRow): CustomPage {
  return {
    id: row.id,
    slug: row.slug,
    title: row.title,
    icon: row.icon,
    blurb: row.blurb,
    content: row.content,
    html: row.html,
    beautified: row.beautified === 1,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

/** Make a string safe for use as a URL slug. */
function slugify(input: string): string {
  return input
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 64) || `page-${randomUUID().slice(0, 8)}`;
}

/** Read all custom pages ordered by creation time. */
pages.get('/', (c) => {
  const rows = db
    .prepare('SELECT * FROM custom_pages ORDER BY created_at ASC')
    .all() as CustomPageRow[];
  return c.json(rows.map(hydrate));
});

/** Read a single page by slug. */
pages.get('/:slug', (c) => {
  const row = db
    .prepare('SELECT * FROM custom_pages WHERE slug = ?')
    .get(c.req.param('slug')) as CustomPageRow | undefined;
  if (!row) return c.json({ error: 'page not found' }, 404);
  return c.json(hydrate(row));
});

interface CreateBody {
  title?: unknown;
  icon?: unknown;
  blurb?: unknown;
  content?: unknown;
}

/** Create a new custom page. */
pages.post('/', async (c) => {
  const body = await c.req.json().catch(() => ({}) as CreateBody);
  const title = typeof body.title === 'string' && body.title.trim() ? body.title.trim() : 'Untitled Page';
  const icon = typeof body.icon === 'string' && body.icon.trim() ? body.icon.trim().slice(0, 4) : '◈';
  const blurb = typeof body.blurb === 'string' ? body.blurb.trim() : '';
  const content = typeof body.content === 'string' ? body.content : '';

  const id = randomUUID();
  const now = new Date().toISOString();

  // Guarantee a unique slug.
  let slug = slugify(title);
  const exists = db.prepare('SELECT 1 FROM custom_pages WHERE slug = ?');
  if (exists.get(slug)) {
    slug = `${slug}-${randomUUID().slice(0, 6)}`;
  }

  const html = content ? renderMarkdown(content) : '';

  db.prepare(
    `INSERT INTO custom_pages (id, slug, title, icon, blurb, content, html, beautified, created_at, updated_at)
     VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?)`,
  ).run(id, slug, title, icon, blurb, content, html, now, now);

  const row = db.prepare('SELECT * FROM custom_pages WHERE id = ?').get(id) as CustomPageRow;
  return c.json(hydrate(row), 201);
});

interface UpdateBody {
  title?: unknown;
  icon?: unknown;
  blurb?: unknown;
  content?: unknown;
}

/** Update a page's editable fields. A beautified page already stores its HTML
 * in `content`, so edits are kept verbatim; an un-beautified page's markdown
 * `content` is re-rendered to HTML whenever it changes. */
pages.patch('/:id', async (c) => {
  const id = c.req.param('id');
  const row = db.prepare('SELECT * FROM custom_pages WHERE id = ?').get(id) as CustomPageRow | undefined;
  if (!row) return c.json({ error: 'page not found' }, 404);

  const body = await c.req.json().catch(() => ({}) as UpdateBody);
  const title = typeof body.title === 'string' && body.title.trim() ? body.title.trim() : row.title;
  const icon = typeof body.icon === 'string' && body.icon.trim() ? body.icon.trim().slice(0, 4) : row.icon;
  const blurb = typeof body.blurb === 'string' ? body.blurb.trim() : row.blurb;
  const content = typeof body.content === 'string' ? body.content : row.content;
  const now = new Date().toISOString();

  // A beautified page's `content` is already the AI-authored HTML, so it is
  // kept verbatim (never re-rendered as markdown). An un-beautified page's
  // `content` is raw markdown and is re-rendered whenever it changes.
  const wasBeautified = row.beautified === 1;
  const contentChanged = body.content !== undefined;
  const beautified = contentChanged && !wasBeautified ? 0 : row.beautified;
  const html = beautified === 1 ? content : renderMarkdown(content);

  db.prepare(
    `UPDATE custom_pages
       SET title = ?, icon = ?, blurb = ?, content = ?, html = ?, beautified = ?, updated_at = ?
     WHERE id = ?`,
  ).run(title, icon, blurb, content, html, beautified, now, id);

  const updated = db.prepare('SELECT * FROM custom_pages WHERE id = ?').get(id) as CustomPageRow;
  return c.json(hydrate(updated));
});

/** Delete a custom page. */
pages.delete('/:id', (c) => {
  const result = db.prepare('DELETE FROM custom_pages WHERE id = ?').run(c.req.param('id'));
  if (result.changes === 0) return c.json({ error: 'page not found' }, 404);
  return c.body(null, 204);
});

/** Beautify a page's content with the AI model, replacing the raw markdown
 * source with the generated HTML (which also becomes the displayed `html`). */
pages.post('/:id/beautify', async (c) => {
  const id = c.req.param('id');
  const row = db.prepare('SELECT * FROM custom_pages WHERE id = ?').get(id) as CustomPageRow | undefined;
  if (!row) return c.json({ error: 'page not found' }, 404);
  if (!row.content.trim()) return c.json({ error: 'cannot beautify an empty page' }, 400);
  if (!isAiConfigured()) {
    return c.json(
      { error: 'AI beautify is not configured. Set OPENAI_API_KEY on the API server.' },
      503,
    );
  }

  let result;
  try {
    result = await beautify(row.content);
  } catch (err) {
    if (err instanceof AiNotConfiguredError) {
      return c.json({ error: err.message }, 503);
    }
    const message = err instanceof Error ? err.message : 'AI beautify failed';
    return c.json({ error: message }, 502);
  }

  const now = new Date().toISOString();
  // The beautified HTML replaces the raw markdown so the page's `content` is
  // the beautified version (the old markdown is discarded).
  db.prepare(
    'UPDATE custom_pages SET content = ?, html = ?, beautified = 1, updated_at = ? WHERE id = ?',
  ).run(result.html, result.html, now, id);

  const updated = db.prepare('SELECT * FROM custom_pages WHERE id = ?').get(id) as CustomPageRow;
  return c.json(hydrate(updated));
});
