import { randomUUID } from 'node:crypto';
import { Router } from 'express';
import { z } from 'zod';
import { requireAuth, type AuthenticatedRequest } from './auth';
import { pool } from './db';
import { notifyUsers } from './notifications';
import { decryptCertificateDocument, encryptCertificateDocument } from './privacy';
import { createCertificatePdf, type CertificateMilestone } from './certificatePdf';

interface JourneyContext {
  id: string;
  status: 'active' | 'closed';
  requestId: string;
  firstUserId: string;
  secondUserId: string;
  firstName: string;
  secondName: string;
  firstGender: 'man' | 'woman' | null;
  secondGender: 'man' | 'woman' | null;
  acceptedAt: Date;
}

interface CertificateRow {
  id: string;
  journeyId: string;
  fileName: string;
  createdAt: Date;
  expiresAt: Date;
}

const certificateMeta = (row: CertificateRow) => ({
  id: row.id, journeyId: row.journeyId, fileName: row.fileName,
  createdAt: row.createdAt, expiresAt: row.expiresAt,
});

async function loadJourneyContext(journeyId: string): Promise<JourneyContext | null> {
  const result = await pool.query<JourneyContext>(
    `SELECT journey.id,journey.status,journey.request_id AS "requestId",
       journey.first_user_id AS "firstUserId",journey.second_user_id AS "secondUserId",
       first_user.display_name AS "firstName",second_user.display_name AS "secondName",
       first_profile.gender AS "firstGender",second_profile.gender AS "secondGender",
       COALESCE((SELECT MIN(event.created_at) FROM relationship_events event
         WHERE event.request_id=request.id AND event.event_type='request_accepted'),
         request.updated_at,journey.created_at) AS "acceptedAt"
     FROM journeys journey
     JOIN engagement_requests request ON request.id=journey.request_id
     JOIN users first_user ON first_user.id=journey.first_user_id
     JOIN users second_user ON second_user.id=journey.second_user_id
     LEFT JOIN profiles first_profile ON first_profile.user_id=journey.first_user_id
     LEFT JOIN profiles second_profile ON second_profile.user_id=journey.second_user_id
     WHERE journey.id=$1`, [journeyId],
  );
  return result.rows[0] ?? null;
}

async function buildCertificatePdf(context: JourneyContext, completedAt: Date): Promise<Buffer> {
  const events = await pool.query<{ event_type: string; actor_user_id: string; details: Record<string, unknown>; created_at: Date }>(
    `SELECT event_type,actor_user_id,details,created_at FROM relationship_events
     WHERE journey_id=$1 AND event_type IN ('photo_approved','photo_shared') ORDER BY created_at`, [context.id],
  );
  const meetings = await pool.query<{ starts_at: Date; session_number: number; duration_seconds: number }>(
    `SELECT meeting.starts_at,meeting.session_number,
       COALESCE((SELECT GREATEST(0,EXTRACT(EPOCH FROM
         (MAX(COALESCE(session.left_at,session.last_seen_at))-MIN(session.joined_at))))::int
         FROM meeting_call_sessions session WHERE session.meeting_id=meeting.id),0) AS duration_seconds
     FROM meeting_proposals meeting WHERE meeting.journey_id=$1 AND meeting.status='completed'
     ORDER BY session_number,starts_at`, [context.id],
  );
  const man = context.firstGender === 'man'
    ? { id: context.firstUserId, name: context.firstName }
    : context.secondGender === 'man' ? { id: context.secondUserId, name: context.secondName }
      : { id: context.firstUserId, name: context.firstName };
  const woman = man.id === context.firstUserId
    ? { id: context.secondUserId, name: context.secondName }
    : { id: context.firstUserId, name: context.firstName };
  const firstPhotoEvent = (ownerId: string) => events.rows.find((item) => item.created_at >= context.acceptedAt
      && (item.details.ownerUserId === ownerId
      || (item.event_type === 'photo_shared' && item.actor_user_id === ownerId)));
  const milestones: CertificateMilestone[] = [
    { label: 'بداية مسار التعارف', occurredAt: context.acceptedAt },
    ...meetings.rows.map((item) => ({ label: `مكالمة التعارف ${item.session_number}`,
      occurredAt: item.starts_at, note: item.duration_seconds > 0
        ? `مدة اللقاء: ${Math.max(1, Math.round(item.duration_seconds / 60))} دقيقة` : undefined })),
    { label: 'إتمام المسار والاتفاق', occurredAt: completedAt },
  ];
  const groomPhoto = firstPhotoEvent(man.id);
  const bridePhoto = firstPhotoEvent(woman.id);
  if (groomPhoto) milestones.push({ label: 'مشاهدة صورة العريس', occurredAt: groomPhoto.created_at });
  if (bridePhoto) milestones.push({ label: 'مشاهدة صورة العروس', occurredAt: bridePhoto.created_at });
  milestones.sort((first, second) => first.occurredAt.getTime() - second.occurredAt.getTime());
  return createCertificatePdf({ groomName: man.name, brideName: woman.name, completedAt, milestones });
}

async function readCertificate(journeyId: string): Promise<CertificateRow | null> {
  const result = await pool.query<CertificateRow>(
    `SELECT id,journey_id AS "journeyId",file_name AS "fileName",created_at AS "createdAt",
       expires_at AS "expiresAt" FROM journey_certificates WHERE journey_id=$1 AND expires_at>NOW()`,
    [journeyId],
  );
  return result.rows[0] ?? null;
}

async function ensurePdfCertificate(row: CertificateRow): Promise<CertificateRow> {
  if (row.fileName.toLowerCase().endsWith('-v4.pdf')) return row;
  const context = await loadJourneyContext(row.journeyId);
  if (!context) throw new Error('Certificate journey context was not found');
  const pdf = await buildCertificatePdf(context, row.createdAt);
  const result = await pool.query<CertificateRow>(
    `UPDATE journey_certificates SET document_encrypted=$1,file_name=$2
     WHERE id=$3 RETURNING id,journey_id AS "journeyId",file_name AS "fileName",
       created_at AS "createdAt",expires_at AS "expiresAt"`,
    [encryptCertificateDocument(pdf.toString('base64')),
      `ala-talabak-certificate-${row.journeyId.slice(0, 8)}-v4.pdf`, row.id],
  );
  return result.rows[0];
}

async function closeJourney(journeyId: string, userId: string): Promise<void> {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    await client.query("UPDATE journeys SET status='closed',updated_at=NOW() WHERE id=$1", [journeyId]);
    await client.query('DELETE FROM journey_contact_grants WHERE journey_id=$1', [journeyId]);
    await client.query("UPDATE family_invitations SET status='revoked' WHERE journey_id=$1 AND status<>'revoked'", [journeyId]);
    await client.query('UPDATE journey_memberships SET is_active=FALSE WHERE journey_id=$1', [journeyId]);
    await client.query("UPDATE decision_rounds SET status='resolved',outcome='closed',resolved_at=NOW() WHERE journey_id=$1 AND status='open'", [journeyId]);
    await client.query("INSERT INTO audit_events (user_id,event_type,details) VALUES ($1,'journey_closed',$2::jsonb)",
      [userId, JSON.stringify({ journeyId })]);
    await client.query('COMMIT');
  } catch (error) { await client.query('ROLLBACK'); throw error; }
  finally { client.release(); }
}

export const journeyCertificatesRouter = Router();

journeyCertificatesRouter.post('/journeys/:id/complete', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    const id = z.uuid().safeParse(request.params.id);
    const parsed = z.object({ outcome: z.enum(['success', 'other']) }).safeParse(request.body);
    if (!id.success || !parsed.success) { response.status(400).json({ error: 'Invalid completion request' }); return; }
    const context = await loadJourneyContext(id.data);
    if (!context || ![context.firstUserId, context.secondUserId].includes(request.authUser!.id)) {
      response.status(404).json({ error: 'Journey not found' }); return;
    }
    if (context.status !== 'active') { response.status(409).json({ error: 'Journey is already closed' }); return; }
    let certificate = await readCertificate(context.id);
    if (parsed.data.outcome === 'success' && !certificate) {
      const certificateId = randomUUID();
      const generatedAt = new Date();
      const pdf = await buildCertificatePdf(context, generatedAt);
      const created = await pool.query<CertificateRow>(
        `INSERT INTO journey_certificates
           (id,journey_id,document_encrypted,file_name,created_by,expires_at)
         VALUES ($1,$2,$3,$4,$5,NOW()+INTERVAL '30 days')
         ON CONFLICT (journey_id) DO UPDATE SET journey_id=EXCLUDED.journey_id
         RETURNING id,journey_id AS "journeyId",file_name AS "fileName",created_at AS "createdAt",
           expires_at AS "expiresAt"`,
        [certificateId, context.id, encryptCertificateDocument(pdf.toString('base64')),
          `ala-talabak-certificate-${context.id.slice(0, 8)}-v4.pdf`, request.authUser!.id],
      );
      certificate = created.rows[0];
      await notifyUsers([context.firstUserId, context.secondUserId], {
        type: 'journey_certificate', title: 'هدية من أسرة عـلــى طــلــبــك',
        body: 'شهادة ذكرى مساركما أصبحت جاهزة للتنزيل لمدة 30 يومًا.',
        journeyId: context.id, requestId: context.requestId,
        dedupeKey: `journey-certificate:${context.id}`,
      });
    } else if (certificate) certificate = await ensurePdfCertificate(certificate);
    await closeJourney(context.id, request.authUser!.id);
    response.json({ closed: true, certificate: certificate ? certificateMeta(certificate) : null });
  });

const authorizeCertificate = async (request: AuthenticatedRequest): Promise<CertificateRow | null> => {
  const id = z.uuid().safeParse(request.params.id);
  if (!id.success) return null;
  const allowed = await pool.query(
    `SELECT 1 FROM journeys WHERE id=$1 AND ($2 IN (first_user_id,second_user_id) OR $3='admin')`,
    [id.data, request.authUser!.id, request.authUser!.role],
  );
  const certificate = allowed.rowCount ? await readCertificate(id.data) : null;
  return certificate ? ensurePdfCertificate(certificate) : null;
};

journeyCertificatesRouter.get('/journeys/:id/certificate', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    const certificate = await authorizeCertificate(request);
    if (!certificate) { response.status(404).json({ error: 'Certificate not found' }); return; }
    response.json({ certificate: certificateMeta(certificate) });
  });

journeyCertificatesRouter.get('/journeys/:id/certificate/download', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    const certificate = await authorizeCertificate(request);
    if (!certificate) { response.status(404).json({ error: 'Certificate not found' }); return; }
    const result = await pool.query<{ document_encrypted: string }>(
      'SELECT document_encrypted FROM journey_certificates WHERE id=$1', [certificate.id],
    );
    response.setHeader('Content-Type', 'application/pdf');
    response.setHeader('Content-Disposition', `attachment; filename="${certificate.fileName}"`);
    response.setHeader('Cache-Control', 'no-store');
    response.send(Buffer.from(decryptCertificateDocument(result.rows[0].document_encrypted), 'base64'));
  });

export async function purgeExpiredJourneyCertificates(): Promise<number> {
  const result = await pool.query('DELETE FROM journey_certificates WHERE expires_at<=NOW()');
  return result.rowCount ?? 0;
}
