import { and, desc, eq, gte, like } from "drizzle-orm";
import { nanoid } from "nanoid";

import { auth } from "../lib/auth";
import { db } from "../lib/db";
import { organization, user, verification } from "../lib/db/schema";

// Better Auth lowercases the email before hashing the verification identifier,
// so we lowercase here too to keep our LIKE query consistent.
const EMAIL = `smoke-auth-${nanoid(8).toLowerCase()}@elevaseo.com`;
const NAME = "Smoke Auth";
const PASSWORD = "SmokeAuth!2026";

/**
 * Better Auth's emailOTP plugin stores each OTP in the `verification` table
 * with identifier = `${type}-otp-${email.toLowerCase()}` and
 * value = `${otp}:${attempts}` (split at last colon). We don't enable
 * `storeOTP: "hashed"` in lib/auth.ts, so the raw OTP is recoverable.
 */
async function findOTPFor(email: string, type: string): Promise<string | null> {
  // Better Auth's `resolveOTP` may insert a fresh row alongside an existing
  // one (the unique-constraint catch path doesn't always trigger). Its
  // internal `findVerificationValue` always returns the LATEST row
  // (ORDER BY createdAt DESC LIMIT 1), so we replicate that here.
  const rows = await db
    .select()
    .from(verification)
    .where(
      and(
        like(verification.identifier, `%${email.toLowerCase()}%`),
        gte(verification.expiresAt, new Date()),
      ),
    )
    .orderBy(desc(verification.createdAt));
  const match = rows.find((r) => r.identifier.includes(type));
  if (!match) return null;
  // value format: "<otp>:<attempts>". Split at the LAST colon to be safe.
  const lastColon = match.value.lastIndexOf(":");
  return lastColon === -1 ? match.value : match.value.slice(0, lastColon);
}

function isResendError(e: unknown): boolean {
  if (!(e instanceof Error)) return false;
  return /RESEND|Resend/.test(e.message);
}

async function main() {
  console.log(`→ Signing up ${EMAIL}`);
  const res = await auth.api.signUpEmail({
    body: { name: NAME, email: EMAIL, password: PASSWORD },
  });
  const userId = res.user.id;

  try {
    // Re-trigger email-verification OTP. RESEND_API_KEY may be missing in
    // dev — the OTP row is created in the verification table BEFORE the
    // Resend send is attempted, so we can still recover the OTP from DB.
    try {
      await auth.api.sendVerificationOTP({
        body: { email: EMAIL, type: "email-verification" },
        headers: new Headers(),
      });
    } catch (e) {
      if (!isResendError(e)) {
        console.warn(`  ! sendVerificationOTP returned an unexpected error:`, e);
      }
    }

    const otp = await findOTPFor(EMAIL, "email-verification");
    if (!otp) {
      throw new Error(
        "No email-verification OTP found in verification table — emailOTP plugin may have changed the identifier format. Inspect manually.",
      );
    }
    console.log(`✓ OTP captured from DB (len=${otp.length})`);

    const verifyResult = await auth.api.verifyEmailOTP({
      body: { email: EMAIL, otp },
    });
    if (!verifyResult || verifyResult.status !== true) {
      throw new Error(`verifyEmailOTP did not return status=true: ${JSON.stringify(verifyResult)}`);
    }
    console.log(`✓ verifyEmailOTP returned status=true`);

    const [u] = await db
      .select({ emailVerified: user.emailVerified })
      .from(user)
      .where(eq(user.id, userId))
      .limit(1);
    if (!u?.emailVerified) {
      throw new Error(`Expected user.emailVerified=true, got ${u?.emailVerified}`);
    }
    console.log(`✓ user.emailVerified = true`);

    // 2. forget-password flow — same path the /forgot-password page uses
    //    (lib/actions/auth.ts → sendVerificationOTP with type "forget-password").
    try {
      await auth.api.sendVerificationOTP({
        body: { email: EMAIL, type: "forget-password" },
        headers: new Headers(),
      });
    } catch (e) {
      if (!isResendError(e)) {
        throw e;
      }
    }
    const resetOtp = await findOTPFor(EMAIL, "forget-password");
    if (!resetOtp) {
      throw new Error("No forget-password OTP found in verification table");
    }
    console.log(`✓ forget-password OTP captured (len=${resetOtp.length})`);

    await auth.api.resetPasswordEmailOTP({
      body: { email: EMAIL, otp: resetOtp, password: "NewSmokePass!2026" },
    });
    console.log(`✓ resetPasswordEmailOTP succeeded`);
  } finally {
    // Cleanup: delete user (cascades member); sweep orphan org.
    await db.delete(user).where(eq(user.id, userId));
    await db.delete(organization).where(eq(organization.name, `Personal — ${EMAIL.toLowerCase()}`));
    // Also clear stale verification rows for this email.
    await db.delete(verification).where(like(verification.identifier, `%${EMAIL.toLowerCase()}%`));
    console.log(`→ Cleanup done`);
  }

  console.log(`✅ verify-auth-flows OK`);
  process.exit(0);
}

main().catch((e) => {
  console.error(`❌ verify-auth-flows FAILED:`, e instanceof Error ? e.message : e);
  process.exit(1);
});
