import bcrypt from 'bcryptjs';
import { Router } from 'express';
import { z } from 'zod';
import { requireAuth, type AuthenticatedRequest } from './auth';
import { pool } from './db';
import { listLiveKitParticipants, listLiveKitRooms, removeLiveKitParticipant } from './liveKitMedia';
import { config } from './config';
import { scheduleAccountDeletion } from './accountLifecycle';

export const accountDeletionRouter = Router();

const disconnectUser = async (userId: string): Promise<void> => {
  if (!config.liveKit.configured) return;
  const rooms = await listLiveKitRooms();
  await Promise.all(rooms.map(async (room) => {
    const participants = await listLiveKitParticipants(room.name);
    await Promise.all(participants.filter((item) => item.identity.startsWith(`${userId}:`))
      .map((item) => removeLiveKitParticipant(room.name, item.identity)));
  }));
};

accountDeletionRouter.delete('/me/account', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    if (request.authUser!.role !== 'user' || request.authUser!.accountType === 'guardian') {
      response.status(403).json({ error: 'Password account required' }); return;
    }
    const parsed = z.object({ password: z.string().min(8).max(128) }).safeParse(request.body);
    if (!parsed.success) { response.status(400).json({ error: 'Password confirmation is required' }); return; }
    const user = await pool.query<{ password_hash: string }>(
      'SELECT password_hash FROM users WHERE id=$1 AND is_active AND deleted_at IS NULL',
      [request.authUser!.id],
    );
    if (!user.rows[0] || !(await bcrypt.compare(parsed.data.password, user.rows[0].password_hash))) {
      response.status(401).json({ error: 'Password confirmation failed' }); return;
    }
    const client = await pool.connect();
    try {
      await client.query('BEGIN');
      const deletion = await scheduleAccountDeletion(client, request.authUser!.id);
      if (!deletion) {
        await client.query('ROLLBACK'); response.status(409).json({ error: 'Account deletion already requested' }); return;
      }
      await client.query(
        "INSERT INTO audit_events (user_id,event_type,details) VALUES ($1,'account_deletion_requested',$2::jsonb)",
        [request.authUser!.id, JSON.stringify({
          purgeAfter: deletion.purgeAfter,
          linkedGuardianUserIds: deletion.guardianUserIds,
        })],
      );
      await client.query('COMMIT');
      const disconnectResults = await Promise.allSettled(deletion.affectedUserIds.map(disconnectUser));
      if (disconnectResults.some((item) => item.status === 'rejected')) {
        console.warn('Some deleted-account media sessions could not be disconnected immediately');
      }
      response.json({
        deleted: true,
        purgeAfter: deletion.purgeAfter,
        linkedGuardianAccountsDeleted: deletion.guardianUserIds.length,
      });
    } catch (error) { await client.query('ROLLBACK'); throw error; }
    finally { client.release(); }
  });

export const purgeExpiredDeletedAccounts = async (): Promise<void> => {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    const expired = await client.query<{ id: string }>(
      "SELECT id FROM users WHERE role='user' AND purge_after<=NOW() FOR UPDATE",
    );
    const ids = expired.rows.map((item) => item.id);
    if (ids.length) {
      await client.query(
        `DELETE FROM journey_call_guardians WHERE participant_user_id=ANY($1::uuid[])
          OR guardian_user_id=ANY($1::uuid[])
          OR verification_id IN (SELECT id FROM guardian_invitations
            WHERE candidate_user_id=ANY($1::uuid[]) OR accepted_by=ANY($1::uuid[]))
          OR exception_id IN (SELECT id FROM guardian_exceptions
            WHERE candidate_user_id=ANY($1::uuid[]) OR representative_user_id=ANY($1::uuid[]))`,
        [ids],
      );
      await client.query(
        "UPDATE guardian_invitations SET status='revoked',accepted_by=NULL WHERE accepted_by=ANY($1::uuid[])",
        [ids],
      );
      await client.query(
        "UPDATE guardian_exceptions SET status='revoked',representative_user_id=NULL WHERE representative_user_id=ANY($1::uuid[])",
        [ids],
      );
      await client.query('DELETE FROM users WHERE id=ANY($1::uuid[])', [ids]);
    }
    await client.query('COMMIT');
  } catch (error) { await client.query('ROLLBACK'); throw error; }
  finally { client.release(); }
};
