import { eq } from 'drizzle-orm';
import { NextRequest, NextResponse } from 'next/server';
import { announcements } from '@/db/schema';
import {
  canCreateContent,
  canTransitionContent,
  getNextContentStatus
} from '@/features/content-approval/lib/workflow';
import {
  AUDIT_ACTION,
  AUDIT_MODULE,
  getRequestMetadata,
  logAuditEvent,
  toAuditValue
} from '@/features/audit-logs/server/audit-service';
import {
  getAnnouncementReviewRecipientIds,
  listAnnouncementsForAccess,
  serializeAnnouncementForAccess,
  serializeAnnouncementInternal
} from '@/features/announcements/server/announcement-data';
import {
  announcementDraftActionSchema,
  announcementDraftSchema
} from '@/features/announcements/schemas/announcement';
import { createContentWorkflowNotification } from '@/features/notifications/server/notification-service';
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
import {
  ANNOUNCEMENT_MAX_FILE_SIZE,
  saveAnnouncementAttachment
} from '@/lib/announcement-storage';
import { getDatabaseErrorMessage } from '@/lib/db-errors';
import { db } from '@/lib/db';

const SUPPORTED_ATTACHMENT_TYPES = new Set([
  'application/pdf',
  'image/jpeg',
  'image/png',
  'application/msword',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  'application/vnd.ms-excel',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
]);

function normalizeDateValue(value: FormDataEntryValue | null) {
  if (typeof value !== 'string') return '';
  return value.trim();
}

function parseBooleanValue(value: FormDataEntryValue | null) {
  return value === 'true';
}

function validateAttachment(file: File | null) {
  if (!file || file.size === 0) return null;

  if (!SUPPORTED_ATTACHMENT_TYPES.has(file.type)) {
    throw new Error('รองรับไฟล์แนบ PDF, JPG, JPEG, PNG, DOC, DOCX, XLS และ XLSX เท่านั้น');
  }

  if (file.size > ANNOUNCEMENT_MAX_FILE_SIZE) {
    throw new Error('ไฟล์แนบต้องมีขนาดไม่เกิน 10MB');
  }

  return file;
}

async function parseAnnouncementFormData(request: NextRequest) {
  const formData = await request.formData();
  const rawValues = {
    title: String(formData.get('title') ?? ''),
    content: String(formData.get('content') ?? ''),
    startDate: normalizeDateValue(formData.get('startDate')),
    endDate: normalizeDateValue(formData.get('endDate')) || undefined,
    removeAttachment: parseBooleanValue(formData.get('removeAttachment'))
  };

  const parsedValues = announcementDraftSchema.safeParse(rawValues);
  if (!parsedValues.success) {
    throw new Error(parsedValues.error.issues[0]?.message ?? 'ข้อมูลประกาศไม่ถูกต้อง');
  }

  const parsedAction = announcementDraftActionSchema.safeParse(
    String(formData.get('action') ?? 'draft')
  );
  if (!parsedAction.success) {
    throw new Error('คำสั่งการบันทึกประกาศไม่ถูกต้อง');
  }

  return {
    action: parsedAction.data,
    values: parsedValues.data,
    attachment: validateAttachment(formData.get('attachment') as File | null)
  };
}

export async function GET(request: NextRequest) {
  try {
    const { organization, session } = await requireOrganizationAccess();
    const { searchParams } = request.nextUrl;

    const result = await listAnnouncementsForAccess({
      organizationId: organization.id,
      permissions: session.user.effectivePermissions,
      currentUserId: session.user.id,
      filters: {
        page: Number(searchParams.get('page') ?? 1),
        limit: Number(searchParams.get('limit') ?? 10),
        search: searchParams.get('search') ?? undefined,
        status: (searchParams.get('status') as
          | 'draft'
          | 'pending_review'
          | 'approved'
          | 'published'
          | 'returned'
          | 'rejected'
          | 'archived'
          | null) ?? undefined,
        pin: (searchParams.get('pin') as 'all' | 'pinned' | null) ?? undefined,
        sort: searchParams.get('sort') ?? undefined
      }
    });

    return NextResponse.json({
      success: true,
      time: new Date().toISOString(),
      message: 'โหลดประกาศเรียบร้อยแล้ว',
      total_announcements: result.total,
      offset: result.offset,
      limit: result.limit,
      announcements: result.announcements
    });
  } catch (error) {
    if (error instanceof AuthError) {
      return NextResponse.json({ message: error.message }, { status: error.status });
    }

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

export async function POST(request: NextRequest) {
  try {
    const { organization, session } = await requireOrganizationAccess();
    const requestMetadata = getRequestMetadata(request);
    const { action, values, attachment } = await parseAnnouncementFormData(request);
    const permissions = session.user.effectivePermissions;

    if (!canCreateContent('announcement', permissions)) {
      return NextResponse.json({ message: 'Forbidden' }, { status: 403 });
    }

    const wantsSubmit = action === 'submit';

    if (
      wantsSubmit &&
      !canTransitionContent({
        module: 'announcement',
        action: 'submit',
        currentStatus: 'draft',
        permissions,
        isOwner: true
      })
    ) {
      return NextResponse.json({ message: 'ไม่มีสิทธิ์ส่งประกาศเพื่อตรวจสอบ' }, { status: 403 });
    }

    const initialStatus = wantsSubmit ? getNextContentStatus('submit') : 'draft';

    const [created] = await db
      .insert(announcements)
      .values({
        organizationId: organization.id,
        title: values.title.trim(),
        content: values.content.trim(),
        startDate: new Date(values.startDate),
        endDate: values.endDate?.trim() ? new Date(values.endDate) : null,
        isPin: false,
        status: initialStatus,
        submittedAt: wantsSubmit ? new Date() : null,
        submittedBy: wantsSubmit ? session.user.id : null,
        createdBy: session.user.id,
        updatedBy: session.user.id
      })
      .returning();

    let record = created;

    if (attachment) {
      const storedAttachment = await saveAnnouncementAttachment({
        file: attachment,
        organizationId: organization.id,
        announcementId: created.id
      });

      [record] = await db
        .update(announcements)
        .set({
          attachmentFileName: storedAttachment.fileName,
          attachmentFileType: storedAttachment.fileType,
          attachmentFileSize: storedAttachment.fileSize,
          attachmentStorageKey: storedAttachment.storageKey,
          attachmentFileUrl: storedAttachment.fileUrl,
          updatedBy: session.user.id,
          updatedAt: new Date()
        })
        .where(eq(announcements.id, created.id))
        .returning();
    }

    const creatorName = session.user.name ?? session.user.email ?? null;
    const announcementRow = {
      ...record,
      createdByName: creatorName,
      updatedByName: creatorName
    };
    const auditAnnouncement = serializeAnnouncementInternal(announcementRow);
    const responseAnnouncement = serializeAnnouncementForAccess({
      row: announcementRow,
      permissions,
      currentUserId: session.user.id
    });

    await logAuditEvent({
      organizationId: organization.id,
      userId: session.user.id,
      module: AUDIT_MODULE.ANNOUNCEMENTS,
      action: AUDIT_ACTION.ANNOUNCEMENT_CREATE,
      entityName: record.title,
      entityId: record.id,
      newValue: toAuditValue(auditAnnouncement),
      ...requestMetadata
    });

    await logAuditEvent({
      organizationId: organization.id,
      userId: session.user.id,
      module: AUDIT_MODULE.ANNOUNCEMENTS,
      action: wantsSubmit
        ? AUDIT_ACTION.ANNOUNCEMENT_SUBMIT
        : AUDIT_ACTION.ANNOUNCEMENT_DRAFT_SAVED,
      entityName: record.title,
      entityId: record.id,
      newValue: toAuditValue(auditAnnouncement),
      ...requestMetadata
    });

    if (wantsSubmit) {
      const reviewerIds = await getAnnouncementReviewRecipientIds({
        organizationId: organization.id,
        excludeUserIds: [session.user.id]
      });

      await Promise.all(
        reviewerIds.map((userId) =>
          createContentWorkflowNotification({
            organizationId: organization.id,
            userId,
            title: `มีประกาศใหม่รอตรวจสอบ: ${record.title}`,
            message: 'มีการส่งประกาศเข้าสู่คิวตรวจสอบ กรุณาเปิดเพื่อตรวจสอบและอนุมัติ',
            type: 'CONTENT_SUBMITTED',
            referenceId: record.id,
            referenceType: 'content_submission'
          })
        )
      );
    }

    return NextResponse.json(
      {
        success: true,
        time: new Date().toISOString(),
        message: wantsSubmit
          ? 'ส่งประกาศเพื่อตรวจสอบเรียบร้อยแล้ว'
          : 'บันทึกร่างประกาศเรียบร้อยแล้ว',
        announcement: responseAnnouncement
      },
      { status: 201 }
    );
  } catch (error) {
    if (error instanceof AuthError) {
      return NextResponse.json({ message: error.message }, { status: error.status });
    }

    if (error instanceof Error && !error.message.includes('Database')) {
      return NextResponse.json({ message: error.message }, { status: 400 });
    }

    console.error('Failed to create announcement', error);
    return NextResponse.json({ message: getDatabaseErrorMessage(error) }, { status: 500 });
  }
}
