// src/server/features/reference_books/lib/access.workflow.ts
// Approval workflow logic for external access entries

import { db } from '@serverShared/db/client';
import { externalAccess, externalAccessPending } from '../db/external_access.table';
import { approvalRequests } from '@features/approval/db/approval_requests.table';
import { employees } from '@serverShared/db/schema';
import { eq, and, or } from 'drizzle-orm';
import { randomUUID } from 'node:crypto';
import { logger } from '@serverShared/lib/logger';
import { notifyAccessUpdate } from './access.notifications';
import { mapToAccessEntry, mapToAccessPendingEntry } from './access.mapper';
import type { ExternalAccessPendingEntry } from '@shared/contracts/access';

export async function approveAccessEntry(pendingId: string): Promise<void> {
  const pendingEntries = await db.select()
    .from(externalAccessPending)
    .where(or(
      eq(externalAccessPending.id, pendingId),
      eq(externalAccessPending.targetId, pendingId),
    ))
    .limit(1);

  if (!pendingEntries[0]) {
    throw new Error('Запись на согласование не найдена');
  }

  const entry = pendingEntries[0];
  const isDeleteRequest = entry.status === 'to_delete';

  await db.transaction(async (tx) => {
    let finalId: string | null = null;

    if (isDeleteRequest) {
      if (entry.targetId) {
        await tx.update(externalAccess)
          .set({ archivedAt: new Date(), updatedAt: new Date() })
          .where(eq(externalAccess.id, entry.targetId));

        logger.info(`[Access] Delete approved (soft delete) for entry: ${entry.resourceName}`);
      }
    } else {
      if (entry.targetId) {
        await tx.update(externalAccess)
          .set({
            resourceName: entry.resourceName,
            link: entry.link,
            login: entry.login,
            password: entry.password,
            comment: entry.comment,
            isApproved: true,
            updatedAt: new Date(),
          })
          .where(eq(externalAccess.id, entry.targetId));

        finalId = entry.targetId;
        logger.info(`[Access] Update approved for entry: ${entry.resourceName}`);
      } else {
        const result = await tx.insert(externalAccess)
          .values({
            id: randomUUID(),
            resourceName: entry.resourceName,
            link: entry.link,
            login: entry.login,
            password: entry.password,
            comment: entry.comment,
            isApproved: true,
            createdBy: entry.createdBy,
            createdAt: new Date(),
            updatedAt: new Date(),
          })
          .$returningId();

        finalId = result[0].id;
        logger.info(`[Access] New entry approved: ${entry.resourceName}`);
      }
    }

    await tx.delete(externalAccessPending).where(eq(externalAccessPending.id, entry.id));

    await tx.delete(approvalRequests).where(and(
      eq(approvalRequests.entityType, 'access'),
      eq(approvalRequests.targetId, entry.id),
    ));

    if (finalId) {
      const finalEntry = await tx.select().from(externalAccess).where(eq(externalAccess.id, finalId)).limit(1);
      if (finalEntry[0]) {
        await notifyAccessUpdate('access:updated', mapToAccessEntry(finalEntry[0]), tx);
      }
    } else {
      await notifyAccessUpdate('access:updated', { action: 'fullRefresh', count: 1 }, tx);
    }
  });
}

export async function rejectAccessEntry(pendingId: string): Promise<void> {
  const pendingEntries = await db.select()
    .from(externalAccessPending)
    .where(or(
      eq(externalAccessPending.id, pendingId),
      eq(externalAccessPending.targetId, pendingId),
    ))
    .limit(1);

  if (!pendingEntries[0]) {
    throw new Error('Запись на согласование не найдена');
  }

  const entry = pendingEntries[0];

  await db.transaction(async (tx) => {
    await tx.delete(externalAccessPending).where(eq(externalAccessPending.id, entry.id));

    await tx.delete(approvalRequests).where(and(
      eq(approvalRequests.entityType, 'access'),
      eq(approvalRequests.targetId, entry.id),
    ));

    logger.info(`[Access] Rejected pending entry: ${entry.id}`);
    await notifyAccessUpdate('access:updated', { action: 'fullRefresh', count: 1 }, tx);
  });
}

export async function getAccessPendingEntryById(id: string): Promise<ExternalAccessPendingEntry | null> {
  const entries = await db.select({
    id: externalAccessPending.id,
    resourceName: externalAccessPending.resourceName,
    link: externalAccessPending.link,
    login: externalAccessPending.login,
    password: externalAccessPending.password,
    comment: externalAccessPending.comment,
    status: externalAccessPending.status,
    targetId: externalAccessPending.targetId,
    createdBy: externalAccessPending.createdBy,
    reviewedBy: externalAccessPending.reviewedBy,
    reviewedAt: externalAccessPending.reviewedAt,
    rejectionReason: externalAccessPending.rejectionReason,
    createdAt: externalAccessPending.createdAt,
    updatedAt: externalAccessPending.updatedAt,
    creatorName: employees.fullName,
  })
    .from(externalAccessPending)
    .leftJoin(employees, eq(externalAccessPending.createdBy, employees.id))
    .where(or(
      eq(externalAccessPending.id, id),
      eq(externalAccessPending.targetId, id),
    ))
    .limit(1);

  if (!entries[0]) {
    return null;
  }

  return mapToAccessPendingEntry(entries[0]);
}

