import { Context } from 'hono';
import { and, eq } from 'drizzle-orm';
import { auth } from './betterAuth.js';
import { db, dbGet } from '../db/index.js';
import { authAccounts } from '../db/auth-schema.js';

// Auth is provided by Better Auth (lib/betterAuth.ts): httpOnly cookie
// sessions validated against the auth_sessions table on every request, so
// revocation (ban/suspend/password reset) applies instantly. This module keeps
// the request-side helpers that the route files use.

// Re-exported for routes that hash/validate passwords outside Better Auth
export { hashPassword, verifyPassword, validatePassword } from './passwordPolicy.js';

export interface AuthUser {
  id: string;
  email: string;
  name: string;
  phone: string | null;
  role: string;
  languagePreference: string | null;
  isClaimed: boolean;
  rucNumber: string | null;
  accountStatus: string;
  emailVerified: boolean;
  image: string | null;
  createdAt: Date | string;
  updatedAt: Date | string;
  /** ID of the Better Auth session backing this request. */
  sessionId: string;
}

/**
 * Resolve the authenticated user for a request from its Better Auth session
 * cookie, or null when there is no valid session. Suspended/unclaimed/banned
 * accounts never get API access even with a live session cookie.
 */
export async function getAuthUser(c: Context): Promise<AuthUser | null> {
  try {
    const session = await auth.api.getSession({ headers: c.req.raw.headers });
    if (!session?.user) {
      return null;
    }

    const user = session.user as any;

    // Suspended (banned) or unclaimed accounts must not retain API access
    if (user.banned) {
      return null;
    }
    if (user.accountStatus && user.accountStatus !== 'active') {
      return null;
    }

    return {
      id: user.id,
      email: user.email,
      name: user.name,
      phone: user.phone ?? null,
      role: user.role ?? 'user',
      languagePreference: user.languagePreference ?? null,
      isClaimed: Boolean(user.isClaimed),
      rucNumber: user.rucNumber ?? null,
      accountStatus: user.accountStatus ?? 'active',
      emailVerified: Boolean(user.emailVerified),
      image: user.image ?? null,
      createdAt: user.createdAt,
      updatedAt: user.updatedAt,
      sessionId: session.session.id,
    };
  } catch {
    return null;
  }
}

export function requireAuth(roles?: string[]) {
  return async (c: Context, next: () => Promise<void>) => {
    const user = await getAuthUser(c);

    if (!user) {
      return c.json({ error: 'Unauthorized' }, 401);
    }

    if (roles && !roles.includes(user.role)) {
      return c.json({ error: 'Forbidden' }, 403);
    }

    c.set('user', user);
    await next();
  };
}

/**
 * Fetch only the credential password hash (never exposed via getAuthUser).
 * Returns null when the user has no password set (Google-only or unclaimed).
 */
export async function getUserPasswordHash(userId: string): Promise<string | null> {
  const row = await dbGet<any>(
    (db as any)
      .select({ password: (authAccounts as any).password })
      .from(authAccounts)
      .where(
        and(
          eq((authAccounts as any).userId, userId),
          eq((authAccounts as any).providerId, 'credential')
        )
      )
  );
  const hash = row?.password;
  return hash && String(hash).length > 0 ? String(hash) : null;
}

/** Whether the user has a linked Google account. */
export async function hasGoogleAccount(userId: string): Promise<boolean> {
  const row = await dbGet<any>(
    (db as any)
      .select({ id: (authAccounts as any).id })
      .from(authAccounts)
      .where(
        and(
          eq((authAccounts as any).userId, userId),
          eq((authAccounts as any).providerId, 'google')
        )
      )
  );
  return !!row;
}
