import bcrypt from "bcryptjs";
import { randomBytes } from "crypto";
import { prisma } from "../../lib/db";
import { makeResponse } from "../../lib/response";
import { ERROR_MESSAGES } from "../../lib/errors";
import { getAdminSettings } from "../../lib/adminSettings";
import { computePreviewCostUsd } from "../../lib/cost";
import { normalizeInstanceType } from "../../lib/instanceTypes";

function requireAdmin(ctr: any) {
  const auth = ctr.getAuth?.();
  if (!auth?.success) return null;
  if (!auth.user.isAdmin) return null;
  return auth.user;
}

export async function listUsers(ctr: any) {
  const admin = requireAdmin(ctr);
  if (!admin) return makeResponse({ ctr, content: { code: 403, message: ERROR_MESSAGES.FORBIDDEN.message } });

  const users = await prisma.user.findMany({
    select: { id: true, username: true, isAdmin: true, isFounder: true, createdAt: true, giteaInstanceUrl: true },
    orderBy: { createdAt: "asc" },
  });

  return makeResponse({ ctr, content: { code: 200, data: users } });
}

export async function createUser(ctr: any) {
  const admin = requireAdmin(ctr);
  if (!admin) return makeResponse({ ctr, content: { code: 403, message: ERROR_MESSAGES.FORBIDDEN.message } });

  const body = await ctr.body();
  const { username, password } = body || {};
  if (!username || !password) return makeResponse({ ctr, content: { code: 400, message: "Username and password required" } });

  const existing = await prisma.user.findUnique({ where: { username } });
  if (existing) return makeResponse({ ctr, content: { code: 409, message: "Username already taken" } });

  const userCount = await prisma.user.count();
  const isFirst = userCount === 0;
  const hash = await bcrypt.hash(password, 12);

  const user = await prisma.user.create({
    data: { username, passwordHash: hash, isAdmin: isFirst, isFounder: isFirst },
    select: { id: true, username: true, isAdmin: true, isFounder: true },
  });

  // Auto-create webhook token for new users
  const webhookSecret = randomBytes(32).toString("hex");
  await prisma.webhookToken.create({ data: { userId: user.id, token: webhookSecret } });

  return makeResponse({ ctr, content: { code: 201, data: user } });
}

export async function updateUser(ctr: any) {
  const admin = requireAdmin(ctr);
  if (!admin) return makeResponse({ ctr, content: { code: 403, message: ERROR_MESSAGES.FORBIDDEN.message } });

  const id = parseInt(ctr.params.get("id") || "0", 10);
  const target = await prisma.user.findUnique({ where: { id } });
  if (!target) return makeResponse({ ctr, content: { code: 404, message: ERROR_MESSAGES.NOT_FOUND.message } });

  const body = await ctr.body();
  const { username, password, isAdmin } = body || {};
  const data: any = {};

  if (username) {
    const existing = await prisma.user.findFirst({ where: { username, id: { not: id } } });
    if (existing) return makeResponse({ ctr, content: { code: 409, message: "Username taken" } });
    data.username = username;
  }
  if (password) data.passwordHash = await bcrypt.hash(password, 12);
  if (isAdmin !== undefined && !target.isFounder) data.isAdmin = Boolean(isAdmin);

  await prisma.user.update({ where: { id }, data });
  return makeResponse({ ctr, content: { code: 200, message: "User updated" } });
}

export async function deleteUser(ctr: any) {
  const admin = requireAdmin(ctr);
  if (!admin) return makeResponse({ ctr, content: { code: 403, message: ERROR_MESSAGES.FORBIDDEN.message } });

  const id = parseInt(ctr.params.get("id") || "0", 10);
  const target = await prisma.user.findUnique({ where: { id } });
  if (!target) return makeResponse({ ctr, content: { code: 404, message: ERROR_MESSAGES.NOT_FOUND.message } });
  if (target.isFounder) return makeResponse({ ctr, content: { code: 403, message: "Cannot delete founder" } });
  if (id === admin.id) return makeResponse({ ctr, content: { code: 403, message: "Cannot delete yourself" } });

  await prisma.user.delete({ where: { id } });
  return makeResponse({ ctr, content: { code: 200, message: "User deleted" } });
}

export async function getSettings(ctr: any) {
  const admin = requireAdmin(ctr);
  if (!admin) return makeResponse({ ctr, content: { code: 403, message: ERROR_MESSAGES.FORBIDDEN.message } });

  const settings = await getAdminSettings();
  return makeResponse({ ctr, content: { code: 200, data: settings } });
}

export async function updateSettings(ctr: any) {
  const admin = requireAdmin(ctr);
  if (!admin) return makeResponse({ ctr, content: { code: 403, message: ERROR_MESSAGES.FORBIDDEN.message } });

  const body = await ctr.body();
  const {
    defaultInstanceType,
    maxConcurrentInstancesPerUser,
    logSizeLimitBytes,
    previewRetentionDays,
    webhookRateLimitPerMinute,
    contactEmail,
  } = body || {};

  const data: any = {};
  if (defaultInstanceType !== undefined) data.defaultInstanceType = normalizeInstanceType(defaultInstanceType);
  if (maxConcurrentInstancesPerUser) data.maxConcurrentInstancesPerUser = Number(maxConcurrentInstancesPerUser);
  if (logSizeLimitBytes) data.logSizeLimitBytes = Number(logSizeLimitBytes);
  if (previewRetentionDays) data.previewRetentionDays = Number(previewRetentionDays);
  if (webhookRateLimitPerMinute) data.webhookRateLimitPerMinute = Number(webhookRateLimitPerMinute);
  if (contactEmail !== undefined) data.contactEmail = contactEmail;

  await prisma.adminSettings.upsert({
    where: { id: 1 },
    update: data,
    create: { id: 1, ...data },
  });

  return makeResponse({ ctr, content: { code: 200, message: "Settings updated" } });
}

export async function adminListPreviews(ctr: any) {
  const admin = requireAdmin(ctr);
  if (!admin) return makeResponse({ ctr, content: { code: 403, message: ERROR_MESSAGES.FORBIDDEN.message } });

  const previews = await prisma.preview.findMany({
    include: {
      repoConfig: { include: { user: { select: { id: true, username: true } } } },
    },
    orderBy: { updatedAt: "desc" },
    take: 200,
  });

  return makeResponse({
    ctr, content: {
      code: 200, data: previews.map(p => ({
        id: p.id,
        prNumber: p.prNumber,
        prTitle: p.prTitle,
        status: p.status,
        stopReason: p.stopReason,
        instanceIp: p.instanceIp,
        port: p.port,
        createdAt: p.createdAt,
        updatedAt: p.updatedAt,
        repoOwner: p.repoConfig.repoOwner,
        repoName: p.repoConfig.repoName,
        user: (p.repoConfig as any).user,
        instanceType: p.instanceType,
        costUsd: computePreviewCostUsd(p),
      }))
    }
  });
}

export async function adminStopPreview(ctr: any) {
  const admin = requireAdmin(ctr);
  if (!admin) return makeResponse({ ctr, content: { code: 403, message: ERROR_MESSAGES.FORBIDDEN.message } });

  const id = parseInt(ctr.params.get("id") || "0", 10);
  const preview = await prisma.preview.findUnique({ where: { id } });
  if (!preview) return makeResponse({ ctr, content: { code: 404, message: ERROR_MESSAGES.NOT_FOUND.message } });

  await prisma.job.create({
    data: { previewId: id, type: "STOP", status: "PENDING", payload: { reason: "Admin stop" } },
  });

  return makeResponse({ ctr, content: { code: 200, message: "Stop job enqueued" } });
}
