import { randomInt, randomUUID } from 'crypto';
import { readPersistentDataFile, writePersistentDataFile } from './local-store';

export type PasswordRecoverySourceMode = 'database' | 'local-fallback';
export type PasswordRecoveryStatus = 'PENDING' | 'COMPLETED' | 'CANCELLED';

export type PasswordRecoveryRequestRecord = {
  id: string;
  userId: string;
  email: string;
  userName: string;
  code: string;
  sourceMode: PasswordRecoverySourceMode;
  status: PasswordRecoveryStatus;
  requestedAt: string;
  expiresAt: string;
  completedAt: string | null;
  cancelledAt: string | null;
};

type CreatePasswordRecoveryRequestInput = {
  userId: string;
  email: string;
  userName: string;
  sourceMode: PasswordRecoverySourceMode;
};

const passwordRecoveryFileName = 'password-recovery-requests.json';
const passwordRecoveryDurationMinutes = 20;

const defaultPasswordRecoveryRequests = () => [] as PasswordRecoveryRequestRecord[];

const normalizeEmail = (email: string) => email.trim().toLowerCase();

const isPendingRequestActive = (request: PasswordRecoveryRequestRecord) =>
  request.status === 'PENDING' && new Date(request.expiresAt).getTime() >= Date.now();

const readPasswordRecoveryRequests = async () =>
  readPersistentDataFile<PasswordRecoveryRequestRecord[]>(passwordRecoveryFileName, defaultPasswordRecoveryRequests);

const writePasswordRecoveryRequests = async (requests: PasswordRecoveryRequestRecord[]) =>
  writePersistentDataFile(passwordRecoveryFileName, requests);

export const serializePasswordRecoveryRequest = ({
  sourceMode: _sourceMode,
  ...request
}: PasswordRecoveryRequestRecord) => request;

export const listActivePasswordRecoveryRequests = async () => {
  const requests = await readPasswordRecoveryRequests();

  return requests
    .filter(isPendingRequestActive)
    .sort((left, right) => new Date(right.requestedAt).getTime() - new Date(left.requestedAt).getTime());
};

export const createPasswordRecoveryRequest = async (input: CreatePasswordRecoveryRequestInput) => {
  const requests = await readPasswordRecoveryRequests();
  const now = new Date();
  const nextRequests = requests.map((request) =>
    request.userId === input.userId && request.status === 'PENDING'
      ? { ...request, status: 'CANCELLED' as const, cancelledAt: now.toISOString() }
      : request,
  );

  const recoveryRequest: PasswordRecoveryRequestRecord = {
    id: randomUUID(),
    userId: input.userId,
    email: normalizeEmail(input.email),
    userName: input.userName.trim(),
    code: String(randomInt(100000, 1000000)),
    sourceMode: input.sourceMode,
    status: 'PENDING',
    requestedAt: now.toISOString(),
    expiresAt: new Date(now.getTime() + passwordRecoveryDurationMinutes * 60 * 1000).toISOString(),
    completedAt: null,
    cancelledAt: null,
  };

  nextRequests.unshift(recoveryRequest);
  await writePasswordRecoveryRequests(nextRequests);
  return recoveryRequest;
};

export const findPasswordRecoveryRequest = async (email: string, code: string) => {
  const normalizedEmail = normalizeEmail(email);
  const normalizedCode = code.trim();
  const requests = await readPasswordRecoveryRequests();

  return (
    requests.find(
      (request) =>
        request.email === normalizedEmail && request.code === normalizedCode && isPendingRequestActive(request),
    ) ?? null
  );
};

export const completePasswordRecoveryRequest = async (requestId: string) => {
  const requests = await readPasswordRecoveryRequests();
  const requestIndex = requests.findIndex((request) => request.id === requestId);

  if (requestIndex === -1) {
    return null;
  }

  const currentRequest = requests[requestIndex]!;

  const updatedRequest: PasswordRecoveryRequestRecord = {
    id: currentRequest.id,
    userId: currentRequest.userId,
    email: currentRequest.email,
    userName: currentRequest.userName,
    code: currentRequest.code,
    sourceMode: currentRequest.sourceMode,
    status: 'COMPLETED',
    requestedAt: currentRequest.requestedAt,
    expiresAt: currentRequest.expiresAt,
    completedAt: new Date().toISOString(),
    cancelledAt: currentRequest.cancelledAt,
  };

  requests[requestIndex] = updatedRequest;
  await writePasswordRecoveryRequests(requests);
  return updatedRequest;
};

export const cancelPasswordRecoveryRequestsForUser = async (userId: string) => {
  const requests = await readPasswordRecoveryRequests();
  const now = new Date().toISOString();
  let hasChanges = false;

  const nextRequests = requests.map((request) => {
    if (request.userId === userId && request.status === 'PENDING') {
      hasChanges = true;
      return {
        ...request,
        status: 'CANCELLED' as const,
        cancelledAt: now,
      };
    }

    return request;
  });

  if (hasChanges) {
    await writePasswordRecoveryRequests(nextRequests);
  }
};
