import { and, eq } from 'drizzle-orm';
import { NextRequest, NextResponse } from 'next/server';
import { trainingRecords, users } from '@/db/schema';
import {
  AUDIT_ACTION,
  AUDIT_MODULE,
  getRequestMetadata,
  logAuditEvent,
  toAuditValue
} from '@/features/audit-logs/server/audit-service';
import {
  createTrainingApprovedNotification,
  createTrainingRejectedNotification
} from '@/features/notifications/server/notification-service';
import type { TrainingRecordReviewPayload } from '@/features/training-records/api/types';
import {
  canReviewTrainingRecords,
  getTrainingRecordByIdForOrganization,
  isMissingTrainingRecordMinutesStorage,
  serializeTrainingRecord
} from '@/features/training-records/server/training-record-data';
import { trainingRecordReviewSchema } from '@/features/training-records/schemas/training-record';
import { minutesToDecimalHours } from '@/features/training-records/utils/time-utils';
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
import { db } from '@/lib/db';

type Params = { params: Promise<{ id: string }> };

export async function PATCH(request: NextRequest, { params }: Params) {
  try {
    const { organization, session, membership } = await requireOrganizationAccess();
    const requestMetadata = getRequestMetadata(request);

    if (!canReviewTrainingRecords(membership.role)) {
      return NextResponse.json({ message: 'คุณไม่มีสิทธิ์ดำเนินการนี้' }, { status: 403 });
    }

    const { id } = await params;
    const parsedBody = trainingRecordReviewSchema.safeParse(
      (await request.json()) as TrainingRecordReviewPayload
    );

    if (!parsedBody.success) {
      const issue = parsedBody.error.issues[0];
      return NextResponse.json(
        { message: issue?.message ?? 'ข้อมูลการตรวจสอบไม่ถูกต้อง' },
        { status: 400 }
      );
    }

    const body = parsedBody.data;

    const existingRecord = await getTrainingRecordByIdForOrganization({
      id: Number(id),
      organizationId: organization.id,
      userId: session.user.id,
      role: membership.role
    });

    if (!existingRecord) {
      return NextResponse.json(
        { success: false, message: `ไม่พบรายการอบรมรหัส ${id}` },
        { status: 404 }
      );
    }

    if (existingRecord.approvalStatus !== 'pending') {
      return NextResponse.json(
        { message: 'สามารถตรวจสอบได้เฉพาะรายการที่อยู่ในสถานะรอตรวจสอบเท่านั้น' },
        { status: 409 }
      );
    }

    const reviewUpdate =
      body.status === 'approved'
        ? {
            approvalStatus: 'approved' as const,
            category: body.category,
            approvedMinutes: body.approvedMinutes,
            approvedHours: minutesToDecimalHours(body.approvedMinutes ?? 0),
            reviewerNote: body.reviewerNote?.trim() || null,
            rejectReason: null,
            reviewedByUserId: session.user.id,
            reviewedBy: session.user.id,
            reviewedAt: new Date(),
            updatedAt: new Date()
          }
        : {
            approvalStatus: 'needs_revision' as const,
            category: null,
            approvedMinutes: null,
            approvedHours: null,
            reviewerNote: body.reviewerNote?.trim() || null,
            rejectReason: body.rejectReason.trim(),
            reviewedByUserId: session.user.id,
            reviewedBy: session.user.id,
            reviewedAt: new Date(),
            updatedAt: new Date()
          };

    let reviewedRecord;

    try {
      [reviewedRecord] = await db
        .update(trainingRecords)
        .set(reviewUpdate)
        .where(and(eq(trainingRecords.id, Number(id)), eq(trainingRecords.organizationId, organization.id)))
        .returning();
    } catch (error) {
      if (!isMissingTrainingRecordMinutesStorage(error)) {
        throw error;
      }

      const legacyReviewUpdate =
        body.status === 'approved'
          ? {
              approvalStatus: 'approved' as const,
              category: body.category,
              approvedHours: minutesToDecimalHours(body.approvedMinutes ?? 0),
              reviewerNote: body.reviewerNote?.trim() || null,
              rejectReason: null,
              reviewedByUserId: session.user.id,
              reviewedBy: session.user.id,
              reviewedAt: new Date(),
              updatedAt: new Date()
            }
          : {
              approvalStatus: 'needs_revision' as const,
              category: null,
              approvedHours: null,
              reviewerNote: body.reviewerNote?.trim() || null,
              rejectReason: body.rejectReason.trim(),
              reviewedByUserId: session.user.id,
              reviewedBy: session.user.id,
              reviewedAt: new Date(),
              updatedAt: new Date()
            };

      [reviewedRecord] = await db
        .update(trainingRecords)
        .set(legacyReviewUpdate)
        .where(and(eq(trainingRecords.id, Number(id)), eq(trainingRecords.organizationId, organization.id)))
        .returning();
    }

    const [reviewer] = await db
      .select({
        id: users.id,
        name: users.name
      })
      .from(users)
      .where(eq(users.id, session.user.id))
      .limit(1);

    if (body.status === 'approved' && existingRecord.userId) {
      await createTrainingApprovedNotification({
        organizationId: organization.id,
        userId: existingRecord.userId,
        courseName: existingRecord.courseName,
        category: reviewedRecord.category,
        approvedHours: reviewedRecord.approvedHours,
        referenceId: reviewedRecord.id
      });
    } else if (existingRecord.userId) {
      await createTrainingRejectedNotification({
        organizationId: organization.id,
        userId: existingRecord.userId,
        courseName: existingRecord.courseName,
        rejectReason: reviewedRecord.rejectReason,
        referenceId: reviewedRecord.id
      });
    }

    const serializedRecord = serializeTrainingRecord({
      ...existingRecord,
      approvalStatus: reviewedRecord.approvalStatus,
      approvedMinutes: reviewedRecord.approvedMinutes,
      approvedHours: reviewedRecord.approvedHours,
      category: reviewedRecord.category,
      reviewerNote: reviewedRecord.reviewerNote,
      rejectReason: reviewedRecord.rejectReason,
      reviewedBy: reviewedRecord.reviewedBy,
      reviewedAt: reviewedRecord.reviewedAt,
      updatedAt: reviewedRecord.updatedAt,
      createdByName: existingRecord.createdByName ?? reviewer?.name ?? null
    });

    await logAuditEvent({
      organizationId: organization.id,
      userId: session.user.id,
      module: AUDIT_MODULE.HRD_REVIEW,
      action: body.status === 'approved' ? AUDIT_ACTION.APPROVE : AUDIT_ACTION.REJECT,
      entityName: existingRecord.courseName,
      entityId: reviewedRecord.id,
      oldValue: toAuditValue(serializeTrainingRecord(existingRecord)),
      newValue: toAuditValue(serializedRecord),
      ...requestMetadata
    });

    return NextResponse.json({
      success: true,
      time: new Date().toISOString(),
      message: 'ตรวจสอบรายการอบรมเรียบร้อยแล้ว',
      training_record: serializedRecord
    });
  } catch (error) {
    if (error instanceof AuthError) {
      return NextResponse.json({ message: error.message }, { status: error.status });
    }

    return NextResponse.json({ message: 'ไม่สามารถตรวจสอบรายการอบรมได้' }, { status: 500 });
  }
}
