import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { db } from '../db/client.js';
import { assertIdentifier } from '../lib/identifier.js';

const columnSchema = z.object({
  name: z.string(),
  type: z.string(),
  nullable: z.boolean().optional(),
  primary: z.boolean().optional(),
  unique: z.boolean().optional(),
  defaultTo: z.any().optional(),
});

const createTableSchema = z.object({
  tableName: z.string(),
  columns: z.array(columnSchema).min(1),
});

const dropTableSchema = z.object({
  tableName: z.string(),
});

const addColumnSchema = z.object({
  tableName: z.string(),
  column: columnSchema,
});

const dropColumnSchema = z.object({
  tableName: z.string(),
  columnName: z.string(),
});

const rawDdlSchema = z.object({
  sql: z.string().min(1),
});

function applyColumn(tableBuilder: any, column: z.infer<typeof columnSchema>) {
  const name = assertIdentifier(column.name, 'column name');
  const definition = tableBuilder.specificType(name, column.type);

  if (column.nullable === false) {
    definition.notNullable();
  }

  if (column.nullable === true) {
    definition.nullable();
  }

  if (column.primary) {
    definition.primary();
  }

  if (column.unique) {
    definition.unique();
  }

  if (column.defaultTo !== undefined) {
    definition.defaultTo(column.defaultTo);
  }
}

export async function registerDdlRoutes(app: FastifyInstance) {
  app.post('/api/ddl/create-table', async (request) => {
    const payload = createTableSchema.parse(request.body);
    const tableName = assertIdentifier(payload.tableName, 'table name');

    await db.schema.createTable(tableName, (table) => {
      payload.columns.forEach((column) => {
        applyColumn(table, column);
      });
    });

    return { ok: true, action: 'create-table', tableName };
  });

  app.post('/api/ddl/drop-table', async (request) => {
    const payload = dropTableSchema.parse(request.body);
    const tableName = assertIdentifier(payload.tableName, 'table name');

    await db.schema.dropTableIfExists(tableName);

    return { ok: true, action: 'drop-table', tableName };
  });

  app.post('/api/ddl/add-column', async (request) => {
    const payload = addColumnSchema.parse(request.body);
    const tableName = assertIdentifier(payload.tableName, 'table name');

    await db.schema.alterTable(tableName, (table) => {
      applyColumn(table, payload.column);
    });

    return { ok: true, action: 'add-column', tableName, column: payload.column.name };
  });

  app.post('/api/ddl/drop-column', async (request) => {
    const payload = dropColumnSchema.parse(request.body);
    const tableName = assertIdentifier(payload.tableName, 'table name');
    const columnName = assertIdentifier(payload.columnName, 'column name');

    await db.schema.alterTable(tableName, (table) => {
      table.dropColumn(columnName);
    });

    return { ok: true, action: 'drop-column', tableName, columnName };
  });

  app.post('/api/ddl/raw', async (request) => {
    const payload = rawDdlSchema.parse(request.body);
    const result = await db.raw(payload.sql);

    return {
      ok: true,
      action: 'raw',
      result,
    };
  });
}
