import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { randomUUID } from "node:crypto";
import { getDb } from "@/lib/db";
import { requireAuth } from "@/lib/auth/middleware";

function assertAdmin(userId: string) {
  const db = getDb();
  const role = db
    .prepare("SELECT id FROM user_roles WHERE user_id = ? AND role = 'admin'")
    .get(userId);
  if (!role) throw new Error("Доступ только для администраторов");
}

export const isAdmin = createServerFn({ method: "GET" })
  .middleware([requireAuth])
  .handler(async ({ context }) => {
    const db = getDb();
    const role = db
      .prepare("SELECT id FROM user_roles WHERE user_id = ? AND role = 'admin'")
      .get(context.userId);
    return { admin: !!role };
  });

export const claimAdmin = createServerFn({ method: "POST" })
  .middleware([requireAuth])
  .handler(async ({ context }) => {
    const db = getDb();
    const count = db.prepare("SELECT COUNT(*) AS c FROM user_roles WHERE role = 'admin'").get() as {
      c: number;
    };
    if (count.c > 0) throw new Error("Администратор уже назначен");
    db.prepare("INSERT INTO user_roles (id, user_id, role) VALUES (?, ?, 'admin')").run(
      randomUUID(),
      context.userId,
    );
    return { ok: true };
  });

export const adminListGames = createServerFn({ method: "GET" })
  .middleware([requireAuth])
  .inputValidator((d: { limit?: number; offset?: number } | undefined) =>
    z
      .object({
        limit: z.number().int().min(1).max(200).optional(),
        offset: z.number().int().min(0).optional(),
      })
      .parse(d ?? {}))
  .handler(async ({ data, context }) => {
    assertAdmin(context.userId);
    const db = getDb();
    const limit = data.limit ?? 100;
    const offset = data.offset ?? 0;
    const total = (db.prepare("SELECT COUNT(*) AS c FROM games").get() as { c: number }).c;
    const rows = db
      .prepare(
        `
        SELECT
          g.id, g.slug, g.title, g.is_featured, g.category_id,
          c.name AS category_name, c.slug AS category_slug
        FROM games g
        LEFT JOIN categories c ON c.id = g.category_id
        ORDER BY g.created_at DESC
        LIMIT ? OFFSET ?
      `,
      )
      .all(limit, offset) as {
      id: string;
      slug: string;
      title: string;
      is_featured: number;
      category_id: string | null;
      category_name: string | null;
      category_slug: string | null;
    }[];

    return {
      total,
      items: rows.map((g) => ({
        id: g.id,
        slug: g.slug,
        title: g.title,
        is_featured: Boolean(g.is_featured),
        category_id: g.category_id,
        categories:
          g.category_slug && g.category_name
            ? { slug: g.category_slug, name: g.category_name }
            : null,
      })),
    };
  });

export const adminListCategories = createServerFn({ method: "GET" })
  .middleware([requireAuth])
  .handler(async ({ context }) => {
    assertAdmin(context.userId);
    const db = getDb();
    return db
      .prepare("SELECT id, slug, name, sort_order FROM categories ORDER BY sort_order")
      .all() as { id: string; slug: string; name: string; sort_order: number }[];
  });

export const adminUpdateGame = createServerFn({ method: "POST" })
  .middleware([requireAuth])
  .inputValidator((d: { id: string; slug: string; title: string; category_id: string | null; is_featured: boolean }) =>
    z
      .object({
        id: z.string().uuid(),
        slug: z
          .string()
          .min(1)
          .max(120)
          .regex(/^[a-z0-9-]+$/, "Только латиница, цифры, дефис"),
        title: z.string().min(1).max(200),
        category_id: z.string().uuid().nullable(),
        is_featured: z.boolean(),
      })
      .parse(d))
  .handler(async ({ data, context }) => {
    assertAdmin(context.userId);
    const db = getDb();
    db.prepare(
      `
      UPDATE games SET
        slug = ?, title = ?, category_id = ?, is_featured = ?, updated_at = datetime('now')
      WHERE id = ?
    `,
    ).run(data.slug, data.title, data.category_id, data.is_featured ? 1 : 0, data.id);
    return { ok: true };
  });

export const adminDeleteGame = createServerFn({ method: "POST" })
  .middleware([requireAuth])
  .inputValidator((d: { id: string }) => z.object({ id: z.string().uuid() }).parse(d))
  .handler(async ({ data, context }) => {
    assertAdmin(context.userId);
    const db = getDb();
    db.prepare("DELETE FROM games WHERE id = ?").run(data.id);
    return { ok: true };
  });

export const adminUpdateCategory = createServerFn({ method: "POST" })
  .middleware([requireAuth])
  .inputValidator((d: { id: string; slug: string; name: string; sort_order: number }) =>
    z
      .object({
        id: z.string().uuid(),
        slug: z.string().min(1).max(80).regex(/^[a-z0-9-]+$/),
        name: z.string().min(1).max(80),
        sort_order: z.number().int(),
      })
      .parse(d))
  .handler(async ({ data, context }) => {
    assertAdmin(context.userId);
    const db = getDb();
    db.prepare(
      `
      UPDATE categories SET slug = ?, name = ?, sort_order = ?, updated_at = datetime('now')
      WHERE id = ?
    `,
    ).run(data.slug, data.name, data.sort_order, data.id);
    return { ok: true };
  });
