import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
import { eq } from 'drizzle-orm';
import { auth } from '../lib/betterAuth.js';
import { validatePassword } from '../lib/passwordPolicy.js';
import { db, dbGet, users } from '../db/index.js';
import { getNow, toDbBool } from '../lib/utils.js';
import { rateLimitMiddleware } from '../lib/rateLimit.js';

// Custom auth flows that Better Auth doesn't provide out of the box. Mounted
// at /api/auth-ext to avoid colliding with Better Auth's /api/auth/* handler.
const authExtRateLimit = rateLimitMiddleware({ max: 20, windowMs: 15 * 60 * 1000, prefix: 'auth-ext' });

const authExt = new Hono();

const claimAccountSchema = z.object({
  password: z.string().min(10, 'Password must be at least 10 characters'),
});

// Complete a progressive-account claim. The user arrives here already holding
// a session established by the claim magic link; this endpoint deliberately
// accepts accountStatus 'unclaimed' sessions (requireAuth would reject them)
// and is the ONLY endpoint that does.
authExt.post('/claim-account', authExtRateLimit, zValidator('json', claimAccountSchema), async (c) => {
  const session = await auth.api.getSession({ headers: c.req.raw.headers });
  if (!session?.user) {
    return c.json({ error: 'Unauthorized. Please use the claim link from your email.' }, 401);
  }

  const user = session.user as any;
  if (user.banned || user.accountStatus === 'suspended') {
    return c.json({ error: 'Account is suspended. Please contact support.' }, 403);
  }
  if (user.isClaimed && user.accountStatus === 'active') {
    return c.json({ error: 'Account is already claimed' }, 400);
  }

  const { password } = c.req.valid('json');
  const passwordValidation = validatePassword(password);
  if (!passwordValidation.valid) {
    return c.json({ error: passwordValidation.error }, 400);
  }

  // Creates the credential account with the argon2id hash from lib/betterAuth.ts
  await auth.api.setPassword({
    body: { newPassword: password },
    headers: c.req.raw.headers,
  });

  // The magic link click proved email ownership
  await (db as any)
    .update(users)
    .set({
      isClaimed: toDbBool(true),
      accountStatus: 'active',
      emailVerified: true,
      updatedAt: getNow(),
    })
    .where(eq((users as any).id, user.id));

  return c.json({
    message: 'Account claimed successfully!',
    user: {
      id: user.id,
      email: user.email,
      name: user.name,
      role: user.role,
      isClaimed: true,
      phone: user.phone ?? null,
      rucNumber: user.rucNumber ?? null,
      languagePreference: user.languagePreference ?? null,
    },
  });
});

// Whether an email belongs to an unclaimed account. Deliberate, rate-limited
// exception to enumeration-safety, matching the legacy register/login UX that
// surfaced "this account can be claimed".
authExt.get('/claim-eligibility', authExtRateLimit, async (c) => {
  const email = c.req.query('email');
  if (!email || !z.string().email().safeParse(email).success) {
    return c.json({ canClaim: false });
  }

  const user = await dbGet<any>(
    (db as any).select().from(users).where(eq((users as any).email, email))
  );

  const canClaim = !!user && !user.banned && user.accountStatus !== 'suspended'
    && (!user.isClaimed || user.accountStatus === 'unclaimed');

  return c.json({ canClaim });
});

export default authExt;
