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 { encrypt, decrypt } from "../../lib/encryption";
import { updateWebhookSecret, validateGiteaUrl, validateGiteaToken } from "../../services/gitea";
import { validateAwsCredentials } from "../../services/ec2";
import { env } from "../../lib/env";

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

async function getOrCreateWebhookToken(userId: number) {
  const existing = await prisma.webhookToken.findUnique({ where: { userId } });
  if (existing) return existing;

  const secret = randomBytes(32).toString("hex");
  return prisma.webhookToken.create({ data: { userId, token: secret } });
}

async function syncRegisteredRepoWebhooks(userId: number, fullUser: any, secret: string) {
  if (!fullUser?.giteaInstanceUrl || !fullUser?.giteaPAT) {
    return { updated: 0, errors: [] as string[] };
  }

  const configs = await prisma.repoConfig.findMany({
    where: { userId, giteaWebhookId: { not: null } },
  });

  const webhookUrl = `${env.PP_BASE_URL}/webhook/${userId}`;
  const errors: string[] = [];
  let updated = 0;

  for (const config of configs) {
    try {
      await updateWebhookSecret(fullUser, config.repoOwner, config.repoName, config.giteaWebhookId!, webhookUrl, secret);
      updated++;
    } catch (e: any) {
      errors.push(`${config.repoOwner}/${config.repoName}: ${e.message}`);
    }
  }

  return { updated, errors };
}

export async function getUserSettings(ctr: any) {
  const user = requireAuth(ctr);
  if (!user) return makeResponse({ ctr, content: { code: ERROR_MESSAGES.UNAUTHORIZED.code, message: ERROR_MESSAGES.UNAUTHORIZED.message } });

  const webhookToken = await prisma.webhookToken.findUnique({ where: { userId: user.id } });
  const fullUser = await prisma.user.findUnique({ where: { id: user.id } });

  return makeResponse({
    ctr, content: {
      code: 200, data: {
        id: user.id,
        username: user.username,
        giteaUsername: fullUser?.giteaUsername,
        giteaInstanceUrl: fullUser?.giteaInstanceUrl,
        giteaPatSet: !!fullUser?.giteaPAT,
        awsAccessKeyId: fullUser?.awsAccessKeyId ? "****" : null,
        awsRegion: fullUser?.awsRegion,
        webhookUrl: `${env.PP_BASE_URL}/webhook/${user.id}`,
        webhookSecret: webhookToken?.token ? "••••••••" : null,
        webhookTokenExists: !!webhookToken,
      }
    }
  });
}

export async function updateUsername(ctr: any) {
  const user = requireAuth(ctr);
  if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
  const body = await ctr.body();
  const { username } = body || {};
  if (!username || typeof username !== "string") return makeResponse({ ctr, content: { code: 400, message: "Username required" } });

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

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

export async function updatePassword(ctr: any) {
  const user = requireAuth(ctr);
  if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
  const body = await ctr.body();
  const { currentPassword, newPassword } = body || {};
  if (!currentPassword || !newPassword) return makeResponse({ ctr, content: { code: 400, message: "Current and new passwords required" } });

  const fullUser = await prisma.user.findUnique({ where: { id: user.id } });
  if (!fullUser) return makeResponse({ ctr, content: { code: 404, message: "User not found" } });

  const valid = await bcrypt.compare(currentPassword, fullUser.passwordHash);
  if (!valid) return makeResponse({ ctr, content: { code: 401, message: "Current password incorrect" } });

  const hash = await bcrypt.hash(newPassword, 12);
  await prisma.user.update({ where: { id: user.id }, data: { passwordHash: hash } });
  return makeResponse({ ctr, content: { code: 200, message: "Password updated" } });
}

export async function updateGitea(ctr: any) {
  const user = requireAuth(ctr);
  if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
  const body = await ctr.body();
  const { giteaInstanceUrl, giteaUsername, giteaPAT } = body || {};

  if (!giteaInstanceUrl || !giteaUsername) {
    return makeResponse({ ctr, content: { code: 400, message: "Gitea URL and username required" } });
  }

  const cleanUrl = giteaInstanceUrl.replace(/\/+$/, "");
  const validation = await validateGiteaUrl(cleanUrl);
  if (!validation.success) {
    return makeResponse({ ctr, content: { code: 400, message: `Gitea validation failed: ${validation.error}` } });
  }

  const data: any = { giteaInstanceUrl: cleanUrl, giteaUsername };
  if (giteaPAT) {
    const tokenCheck = await validateGiteaToken(cleanUrl, giteaPAT, giteaUsername);
    if (!tokenCheck.success) {
      return makeResponse({ ctr, content: { code: 400, message: `Gitea token rejected: ${tokenCheck.error}` } });
    }
    data.giteaPAT = encrypt(giteaPAT);
  }

  const updatedUser = await prisma.user.update({ where: { id: user.id }, data });
  const webhookToken = await getOrCreateWebhookToken(user.id);
  const hookSync = await syncRegisteredRepoWebhooks(user.id, updatedUser, webhookToken.token);

  return makeResponse({
    ctr, content: {
      code: 200,
      message: hookSync.errors.length
        ? `Connected to Gitea ${validation.version}; ${hookSync.updated} webhooks updated, ${hookSync.errors.length} failed`
        : hookSync.updated > 0
          ? `Connected to Gitea ${validation.version}; ${hookSync.updated} webhooks updated`
          : `Connected to Gitea ${validation.version}`,
      data: { version: validation.version, hookSync },
    }
  });
}

export async function updateAws(ctr: any) {
  const user = requireAuth(ctr);
  if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });
  const body = await ctr.body();
  const awsAccessKeyId = typeof body?.awsAccessKeyId === "string" ? body.awsAccessKeyId.trim() : body?.awsAccessKeyId;
  const awsSecretAccessKey = typeof body?.awsSecretAccessKey === "string" ? body.awsSecretAccessKey.trim() : body?.awsSecretAccessKey;
  const awsRegion = typeof body?.awsRegion === "string" ? body.awsRegion.trim() : body?.awsRegion;

  if (!awsAccessKeyId || !awsSecretAccessKey || !awsRegion) {
    return makeResponse({ ctr, content: { code: 400, message: "AWS credentials and region required" } });
  }

  const tempUser = {
    ...user,
    awsAccessKeyId: encrypt(awsAccessKeyId),
    awsSecretAccessKey: encrypt(awsSecretAccessKey),
    awsRegion,
  };
  const validation = await validateAwsCredentials(tempUser as any);
  if (!validation.success) {
    return makeResponse({ ctr, content: { code: 400, message: `AWS validation failed: ${validation.error}` } });
  }

  await prisma.user.update({
    where: { id: user.id },
    data: {
      awsAccessKeyId: encrypt(awsAccessKeyId),
      awsSecretAccessKey: encrypt(awsSecretAccessKey),
      awsRegion,
    },
  });

  return makeResponse({ ctr, content: { code: 200, message: `Connected as ${validation.arn}`, data: { arn: validation.arn } } });
}

export async function getWebhookSecret(ctr: any) {
  const user = requireAuth(ctr);
  if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });

  const token = await getOrCreateWebhookToken(user.id);

  return makeResponse({ ctr, content: { code: 200, data: { token: token.token, webhookUrl: `${env.PP_BASE_URL}/webhook/${user.id}` } } });
}

export async function regenerateWebhookSecret(ctr: any) {
  const user = requireAuth(ctr);
  if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } });

  const newSecret = randomBytes(32).toString("hex");
  await prisma.webhookToken.upsert({
    where: { userId: user.id },
    update: { token: newSecret },
    create: { userId: user.id, token: newSecret },
  });

  const fullUser = await prisma.user.findUnique({ where: { id: user.id } });
  if (!fullUser?.giteaInstanceUrl) {
    return makeResponse({ ctr, content: { code: 200, message: "Secret regenerated (no Gitea hooks to update)", data: { token: newSecret } } });
  }

  const hookSync = await syncRegisteredRepoWebhooks(user.id, fullUser, newSecret);

  return makeResponse({
    ctr, content: {
      code: 200,
      message: hookSync.errors.length ? `Secret regenerated with ${hookSync.errors.length} hook update errors` : "Secret regenerated and all hooks updated",
      data: { token: newSecret, errors: hookSync.errors, hookSync },
    }
  });
}
