import type { RowDataPacket } from "mysql2/promise";
import { createId } from "@/db/ids";
import {
  dualWriteEmailVerificationToken,
  dualWriteMarkEmailVerificationUsed,
  dualWriteMarkPasswordResetUsed,
  dualWritePasswordResetToken,
  dualWriteRefreshToken,
  dualWriteRevokeAllRefreshForUser,
  dualWriteRevokeRefreshByHash,
  dualWriteRevokeRefreshById,
} from "@/db/dualWrite";
import { connectMysql, mysqlPool, sqlExecute, sqlQuery } from "./pool";

async function ensure() {
  try {
    mysqlPool();
  } catch {
    await connectMysql();
  }
}

export async function createRefreshTokenMysql(input: {
  userId: string;
  tokenHash: string;
  expiresAt: Date;
}) {
  await ensure();
  const id = createId();
  await sqlExecute(
    `INSERT INTO refresh_tokens (id, user_id, token_hash, expires_at, revoked_at, created_at)
     VALUES (?,?,?,?,NULL,?)`,
    [id, input.userId, input.tokenHash, input.expiresAt, new Date()],
  );
  await dualWriteRefreshToken({
    id,
    userId: input.userId,
    tokenHash: input.tokenHash,
    expiresAt: input.expiresAt,
  });
  return id;
}

export async function findRefreshTokenByHashMysql(tokenHash: string) {
  await ensure();
  const rows = await sqlQuery<RowDataPacket[]>(
    `SELECT * FROM refresh_tokens WHERE token_hash = ? LIMIT 1`,
    [tokenHash],
  );
  if (!rows[0]) return null;
  const r = rows[0];
  return {
    id: String(r.id),
    userId: String(r.user_id),
    tokenHash: String(r.token_hash),
    expiresAt: new Date(r.expires_at),
    revokedAt: r.revoked_at ? new Date(r.revoked_at) : null,
  };
}

export async function revokeRefreshTokenMysql(id: string) {
  await ensure();
  await sqlExecute(`UPDATE refresh_tokens SET revoked_at = ? WHERE id = ?`, [new Date(), id]);
  await dualWriteRevokeRefreshById(id);
}

export async function revokeRefreshTokenByHashMysql(tokenHash: string) {
  await ensure();
  await sqlExecute(
    `UPDATE refresh_tokens SET revoked_at = ? WHERE token_hash = ? AND revoked_at IS NULL`,
    [new Date(), tokenHash],
  );
  await dualWriteRevokeRefreshByHash(tokenHash);
}

export async function revokeAllRefreshTokensForUserMysql(userId: string) {
  await ensure();
  await sqlExecute(
    `UPDATE refresh_tokens SET revoked_at = ? WHERE user_id = ? AND revoked_at IS NULL`,
    [new Date(), userId],
  );
  await dualWriteRevokeAllRefreshForUser(userId);
}

export async function createPasswordResetTokenMysql(input: {
  userId: string;
  tokenHash: string;
  expiresAt: Date;
}) {
  await ensure();
  const id = createId();
  await sqlExecute(
    `INSERT INTO password_reset_tokens (id, user_id, token_hash, expires_at, used_at, created_at)
     VALUES (?,?,?,?,NULL,?)`,
    [id, input.userId, input.tokenHash, input.expiresAt, new Date()],
  );
  await dualWritePasswordResetToken({
    id,
    userId: input.userId,
    tokenHash: input.tokenHash,
    expiresAt: input.expiresAt,
  });
  return id;
}

export async function findPasswordResetByHashMysql(tokenHash: string) {
  await ensure();
  const rows = await sqlQuery<RowDataPacket[]>(
    `SELECT * FROM password_reset_tokens WHERE token_hash = ? LIMIT 1`,
    [tokenHash],
  );
  if (!rows[0]) return null;
  const r = rows[0];
  return {
    id: String(r.id),
    userId: String(r.user_id),
    tokenHash: String(r.token_hash),
    expiresAt: new Date(r.expires_at),
    usedAt: r.used_at ? new Date(r.used_at) : null,
  };
}

export async function markPasswordResetUsedMysql(id: string) {
  await ensure();
  await sqlExecute(`UPDATE password_reset_tokens SET used_at = ? WHERE id = ?`, [
    new Date(),
    id,
  ]);
  await dualWriteMarkPasswordResetUsed(id);
}

export async function createEmailVerificationTokenMysql(input: {
  userId: string;
  tokenHash: string;
  expiresAt: Date;
}) {
  await ensure();
  const id = createId();
  await sqlExecute(
    `INSERT INTO email_verification_tokens (id, user_id, token_hash, expires_at, used_at, created_at)
     VALUES (?,?,?,?,NULL,?)`,
    [id, input.userId, input.tokenHash, input.expiresAt, new Date()],
  );
  await dualWriteEmailVerificationToken({
    id,
    userId: input.userId,
    tokenHash: input.tokenHash,
    expiresAt: input.expiresAt,
  });
  return id;
}

export async function findEmailVerificationByHashMysql(tokenHash: string) {
  await ensure();
  const rows = await sqlQuery<RowDataPacket[]>(
    `SELECT * FROM email_verification_tokens WHERE token_hash = ? LIMIT 1`,
    [tokenHash],
  );
  if (!rows[0]) return null;
  const r = rows[0];
  return {
    id: String(r.id),
    userId: String(r.user_id),
    expiresAt: new Date(r.expires_at),
    usedAt: r.used_at ? new Date(r.used_at) : null,
  };
}

export async function markEmailVerificationUsedMysql(id: string) {
  await ensure();
  await sqlExecute(`UPDATE email_verification_tokens SET used_at = ? WHERE id = ?`, [
    new Date(),
    id,
  ]);
  await dualWriteMarkEmailVerificationUsed(id);
}
