import { createHash, randomUUID } from 'node:crypto';
import type { PoolClient } from 'pg';
import { pool } from './db';

type GuardianSource = {
  guardianUserId: string;
  mode: 'guardian' | 'exception';
  verificationId: string | null;
  exceptionId: string | null;
};

const findGuardian = async (
  client: PoolClient, participantId: string, excludedIds: string[],
): Promise<GuardianSource | null> => {
  const guardian = await client.query<{ id: string; guardian_user_id: string }>(
    `SELECT i.id,i.accepted_by AS guardian_user_id
     FROM guardian_invitations i JOIN users u ON u.id=i.accepted_by
     WHERE i.candidate_user_id=$1 AND i.status='verified'
       AND u.is_active AND u.deleted_at IS NULL AND NOT (u.id=ANY($2::uuid[]))
     ORDER BY i.completed_at DESC NULLS LAST,i.created_at DESC LIMIT 1`,
    [participantId, excludedIds],
  );
  if (guardian.rows[0]) return {
    guardianUserId: guardian.rows[0].guardian_user_id,
    mode: 'guardian', verificationId: guardian.rows[0].id, exceptionId: null,
  };
  const representative = await client.query<{ id: string; representative_user_id: string }>(
    `SELECT e.id,e.representative_user_id
     FROM guardian_exceptions e
     JOIN guardian_representative_invitations ri ON ri.exception_id=e.id
       AND ri.representative_user_id=e.representative_user_id AND ri.status='accepted'
     JOIN users u ON u.id=e.representative_user_id
     WHERE e.candidate_user_id=$1 AND e.status='approved'
       AND e.contact_confirmed_at IS NOT NULL
       AND u.is_active AND u.deleted_at IS NULL AND u.account_type='representative'
       AND NOT (u.id=ANY($2::uuid[]))
     ORDER BY e.reviewed_at DESC NULLS LAST,e.created_at DESC LIMIT 1`,
    [participantId, excludedIds],
  );
  return representative.rows[0] ? {
    guardianUserId: representative.rows[0].representative_user_id,
    mode: 'exception', verificationId: null, exceptionId: representative.rows[0].id,
  } : null;
};

const ensureMembership = async (
  client: PoolClient, journeyId: string, participantId: string, source: GuardianSource,
): Promise<void> => {
  const roleLabel = source.mode === 'guardian' ? 'ولي الأمر' : 'الممثل البديل';
  const membership = await client.query(
    'SELECT 1 FROM journey_memberships WHERE journey_id=$1 AND user_id=$2',
    [journeyId, source.guardianUserId],
  );
  if (membership.rowCount) {
    await client.query(
      `UPDATE journey_memberships SET is_active=TRUE,permission='viewer',role_label=$3
       WHERE journey_id=$1 AND user_id=$2`,
      [journeyId, source.guardianUserId, roleLabel],
    );
  } else {
    const invitationId = randomUUID();
    await client.query(
      `INSERT INTO family_invitations
         (id,journey_id,invited_by,code_hash,role_label,permission,status,expires_at,accepted_by,accepted_at)
       VALUES ($1,$2,$3,$4,$5,'viewer','accepted',NOW()+INTERVAL '100 years',$6,NOW())`,
      [invitationId, journeyId, participantId,
        createHash('sha256').update(randomUUID()).digest('hex'), roleLabel, source.guardianUserId],
    );
    await client.query(
      `INSERT INTO journey_memberships
         (journey_id,user_id,invitation_id,role_label,permission)
       VALUES ($1,$2,$3,$4,'viewer')`,
      [journeyId, source.guardianUserId, invitationId, roleLabel],
    );
  }
  await client.query(
    `INSERT INTO journey_call_guardians
       (journey_id,participant_user_id,guardian_user_id,selection_mode,verification_id,exception_id)
     VALUES ($1,$2,$3,$4,$5,$6)
     ON CONFLICT (journey_id,participant_user_id) DO UPDATE
       SET guardian_user_id=EXCLUDED.guardian_user_id,
           selection_mode=EXCLUDED.selection_mode,
           verification_id=EXCLUDED.verification_id,
           exception_id=EXCLUDED.exception_id,updated_at=NOW()`,
    [journeyId, participantId, source.guardianUserId, source.mode,
      source.verificationId, source.exceptionId],
  );
};

export const autoAssignJourneyGuardians = async (journeyId: string): Promise<void> => {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    await client.query('SELECT pg_advisory_xact_lock(hashtext($1))', [`journey-guardians:${journeyId}`]);
    const journey = await client.query<{ first_user_id: string; second_user_id: string }>(
      'SELECT first_user_id,second_user_id FROM journeys WHERE id=$1 FOR UPDATE', [journeyId],
    );
    if (!journey.rows[0]) { await client.query('ROLLBACK'); return; }
    const participants = [journey.rows[0].first_user_id, journey.rows[0].second_user_id];
    const usedIds = [...participants];
    for (const participantId of participants) {
      const source = await findGuardian(client, participantId, usedIds);
      if (!source) continue;
      await ensureMembership(client, journeyId, participantId, source);
      usedIds.push(source.guardianUserId);
    }
    await client.query('COMMIT');
  } catch (error) {
    await client.query('ROLLBACK'); throw error;
  } finally { client.release(); }
};

export const autoAssignGuardiansForCandidate = async (candidateId: string): Promise<void> => {
  const journeys = await pool.query<{ id: string }>(
    `SELECT DISTINCT j.id FROM journeys j
     JOIN meeting_proposals m ON m.journey_id=j.id AND m.status='confirmed'
     WHERE j.status='active' AND (j.first_user_id=$1 OR j.second_user_id=$1)`,
    [candidateId],
  );
  await Promise.all(journeys.rows.map((journey) => autoAssignJourneyGuardians(journey.id)));
};
