import { and, eq } from 'drizzle-orm';
import { NextRequest, NextResponse } from 'next/server';
import { trainingRecords } from '@/db/schema';
import {
  AUDIT_ACTION,
  AUDIT_MODULE,
  getRequestMetadata,
  logAuditEvent,
  toAuditValue
} from '@/features/audit-logs/server/audit-service';
import type { TrainingRecordMutationPayload } from '@/features/training-records/api/types';
import {
  assertUserAndCourseAccess,
  canEmployeeManageEditableRecord,
  canReviewTrainingRecords,
  getTrainingRecordByIdForOrganization,
  isMissingTrainingRecordMinutesStorage,
  normalizeTrainingRecordPayload,
  serializeTrainingRecord
} from '@/features/training-records/server/training-record-data';
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
import { db } from '@/lib/db';
import { deleteStoredCertificate } from '@/lib/certificate-storage';

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

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

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

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

    return NextResponse.json({
      success: true,
      time: new Date().toISOString(),
      message: `โหลดรายการอบรมรหัส ${id} เรียบร้อยแล้ว`,
      training_record: serializeTrainingRecord(record)
    });
  } catch (error) {
    if (error instanceof AuthError) {
      return NextResponse.json({ message: error.message }, { status: error.status });
    }

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

export async function PATCH(request: NextRequest, { params }: Params) {
  try {
    const { organization, membership, session } = await requireOrganizationAccess();
    const requestMetadata = getRequestMetadata(request);
    const { id } = await params;
    const body = normalizeTrainingRecordPayload(
      (await request.json()) as TrainingRecordMutationPayload
    );

    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 (
      !(await canEmployeeManageEditableRecord({
        organizationId: organization.id,
        role: membership.role,
        recordUserId: existingRecord.userId,
        recordEmployeeId: existingRecord.employeeId,
        actorUserId: session.user.id,
        approvalStatus: existingRecord.approvalStatus
      }))
    ) {
      return NextResponse.json(
        { message: 'พนักงานสามารถแก้ไขได้เฉพาะรายการที่อยู่ในสถานะฉบับร่างหรือขอแก้ไขเท่านั้น' },
        { status: 403 }
      );
    }

    if (
      existingRecord.approvalStatus === 'approved' &&
      !canReviewTrainingRecords(membership.role)
    ) {
      return NextResponse.json(
        { message: 'ผู้ใช้นี้ไม่สามารถแก้ไขรายการที่อนุมัติแล้วได้' },
        { status: 409 }
      );
    }

    const { user, course } = await assertUserAndCourseAccess({
      organizationId: organization.id,
      targetEmployeeId: body.employeeId,
      actorUserId: session.user.id,
      actorRole: membership.role,
      courseId: body.courseId,
      courseName: body.courseName
    });

    if (!user) {
      return NextResponse.json(
        { message: 'ไม่พบผู้ใช้งานในองค์กรที่กำลังใช้งานอยู่' },
        { status: 400 }
      );
    }

    const approvalStatus = body.submissionMode === 'draft' ? 'draft' : 'pending';
    const reviewReset =
      body.submissionMode === 'submit'
        ? {
            category: null,
            approvedMinutes: null,
            approvedHours: null,
            reviewerNote: null,
            rejectReason: null,
            reviewedByUserId: null,
            reviewedBy: null,
            reviewedAt: null
          }
        : {};

    let record;

    try {
      [record] = await db
        .update(trainingRecords)
        .set({
          userId: user.userId ?? null,
          employeeId: body.employeeId,
          courseId: course?.id ?? null,
          courseName: course?.name ?? body.courseName,
          trainingDate: new Date(body.trainingDate),
          trainingType: body.trainingType,
          submittedMinutes: body.submittedMinutes,
          hours: body.hours,
          organizer: body.organizer,
          note: body.note,
          approvalStatus,
          ...reviewReset,
          updatedAt: new Date()
        })
        .where(and(eq(trainingRecords.id, Number(id)), eq(trainingRecords.organizationId, organization.id)))
        .returning();
    } catch (error) {
      if (!isMissingTrainingRecordMinutesStorage(error)) {
        throw error;
      }

      [record] = await db
        .update(trainingRecords)
        .set({
          userId: user.userId ?? null,
          employeeId: body.employeeId,
          courseId: course?.id ?? null,
          courseName: course?.name ?? body.courseName,
          trainingDate: new Date(body.trainingDate),
          trainingType: body.trainingType,
          hours: body.hours,
          organizer: body.organizer,
          note: body.note,
          approvalStatus,
          ...reviewReset,
          updatedAt: new Date()
        })
        .where(and(eq(trainingRecords.id, Number(id)), eq(trainingRecords.organizationId, organization.id)))
        .returning();
    }

    const serializedRecord = serializeTrainingRecord({
      id: record.id,
      organizationId: record.organizationId,
      userId: record.userId,
      employeeId: record.employeeId,
      employeeCode: user.employeeCode ?? existingRecord.employeeCode ?? null,
      userName: user.fullName,
      companyName: existingRecord.companyName,
      departmentName: existingRecord.departmentName,
      courseId: record.courseId,
      courseName: record.courseName,
      trainingDate: record.trainingDate,
      trainingType: record.trainingType,
      submittedMinutes: record.submittedMinutes,
      hours: record.hours,
      approvedMinutes: record.approvedMinutes,
      approvedHours: record.approvedHours,
      category: record.category,
      organizer: record.organizer,
      note: record.note,
      certificatePreviewUrl: existingRecord.certificatePreviewUrl,
      certificatePreviewType: existingRecord.certificatePreviewType,
      approvalStatus: record.approvalStatus,
      reviewerNote: record.reviewerNote,
      rejectReason: record.rejectReason,
      reviewedBy: record.reviewedBy,
      reviewedAt: record.reviewedAt,
      createdBy: record.createdBy,
      createdByName: existingRecord.createdByName,
      createdAt: record.createdAt,
      updatedAt: record.updatedAt
    });

    await logAuditEvent({
      organizationId: organization.id,
      userId: session.user.id,
      module: AUDIT_MODULE.TRAINING_RECORDS,
      action: AUDIT_ACTION.UPDATE,
      entityName: record.courseName,
      entityId: record.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 });
  }
}

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

    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 (
      !(await canEmployeeManageEditableRecord({
        organizationId: organization.id,
        role: membership.role,
        recordUserId: existingRecord.userId,
        recordEmployeeId: existingRecord.employeeId,
        actorUserId: session.user.id,
        approvalStatus: existingRecord.approvalStatus
      }))
    ) {
      return NextResponse.json(
        { message: 'พนักงานสามารถลบได้เฉพาะรายการที่อยู่ในสถานะฉบับร่างหรือขอแก้ไขเท่านั้น' },
        { status: 403 }
      );
    }

    const recordCertificates = await db.query.certificates.findMany({
      where: (table, { and, eq }) =>
        and(eq(table.trainingRecordId, Number(id)), eq(table.organizationId, organization.id))
    });

    for (const certificate of recordCertificates) {
      await deleteStoredCertificate(certificate.storageKey);
    }

    await db
      .delete(trainingRecords)
      .where(and(eq(trainingRecords.id, Number(id)), eq(trainingRecords.organizationId, organization.id)));

    await logAuditEvent({
      organizationId: organization.id,
      userId: session.user.id,
      module: AUDIT_MODULE.TRAINING_RECORDS,
      action: AUDIT_ACTION.DELETE,
      entityName: existingRecord.courseName,
      entityId: existingRecord.id,
      oldValue: toAuditValue(serializeTrainingRecord(existingRecord)),
      ...requestMetadata
    });

    return NextResponse.json({
      success: true,
      message: 'ลบรายการอบรมเรียบร้อยแล้ว'
    });
  } catch (error) {
    if (error instanceof AuthError) {
      return NextResponse.json({ message: error.message }, { status: error.status });
    }

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