"use server"

import { createId } from "@paralleldrive/cuid2"

import {
  requirePermission,
  checkPermission,
  PermissionDeniedError,
} from "@/shared/lib/auth-guard"
import { trackEvent } from "@/shared/lib/track-event"
import { logAudit } from "@/shared/lib/audit-logger"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { storageProvider } from "@/shared/lib/storage-provider"
import {
  generateStoragePath,
  isAllowedMimeType,
  MAX_FILE_SIZE,
} from "@/shared/lib/file-storage"

import { UploadMetadataSchema, BatchDeleteSchema, FileListQuerySchema } from "./schema"
import {
  createFileAttachment,
  getFileAttachment,
  getFileAttachmentsWithFilters,
  getFileStats,
  getFileAttachmentsByIds,
  deleteFileAttachment,
  deleteFileAttachments,
} from "./data-access"
import type {
  FileAttachment,
  FileUploadResult,
  FileStats as FileStatsType,
  FileAttachmentQueryParams,
  BatchDeleteResult,
} from "./types"

function handleActionError(e: unknown): ActionState<never> {
  if (e instanceof PermissionDeniedError) {
    return { success: false, message: e.message }
  }
  if (e instanceof Error) return { success: false, message: e.message }
  return { success: false, message: "Unexpected error" }
}

/**
 * Upload a file: persist to storage + create DB record.
 *
 * Requires `FILE_UPLOAD` permission. Performs Zod-validated metadata,
 * MIME/size checks, and writes to disk via the storageProvider abstraction.
 * Records `file.uploaded` track event + audit log entry.
 *
 * @returns ActionState<FileUploadResult>
 */
export async function uploadFileAction(
  file: File,
  rawMetadata: { targetType?: string | null; targetId?: string | null }
): Promise<ActionState<FileUploadResult>> {
  try {
    const ctx = await requirePermission(Permissions.FILE_UPLOAD)

    const meta = UploadMetadataSchema.parse(rawMetadata)

    if (file.size === 0) {
      return { success: false, message: "File is empty" }
    }
    if (file.size > MAX_FILE_SIZE) {
      return { success: false, message: "File size exceeds 10MB limit" }
    }
    const mimeType = file.type || "application/octet-stream"
    if (!isAllowedMimeType(mimeType)) {
      return { success: false, message: `File type ${mimeType} is not allowed` }
    }

    const originalName = file.name || "unnamed"
    const storagePath = generateStoragePath(originalName)
    const bytes = Buffer.from(await file.arrayBuffer())
    const url = await storageProvider.save(bytes, storagePath)

    const id = createId()
    const filename = storagePath.split("/").pop() ?? id
    const created = await createFileAttachment({
      id,
      filename,
      originalName,
      mimeType,
      size: file.size,
      storagePath,
      url,
      uploaderId: ctx.userId,
      targetType: meta.targetType ?? null,
      targetId: meta.targetId,
    })

    if (!created) {
      return { success: false, message: "Failed to persist file record" }
    }

    await trackEvent({
      event: "file.uploaded",
      userId: ctx.userId,
      targetId: id,
      targetType: "file",
      properties: {
        filename: originalName,
        mimeType,
        size: file.size,
        targetType: meta.targetType ?? null,
      },
    })

    await logAudit({
      action: "upload",
      module: "files",
      targetId: id,
      targetType: "file",
      detail: { filename: originalName, mimeType, size: file.size },
    })

    const result: FileUploadResult = {
      id: created.id,
      url: created.url ?? url,
      filename: created.filename,
      originalName: created.originalName,
      size: created.size,
      mimeType: created.mimeType,
    }

    return { success: true, data: result }
  } catch (e) {
    await trackEvent({
      event: "file.upload_failed",
      targetType: "file",
      properties: { reason: e instanceof Error ? e.message : "unknown" },
    }).catch(() => undefined)
    return handleActionError(e)
  }
}

/**
 * Get a single file by ID.
 *
 * Requires `FILE_READ` permission. Non-admin users (those without
 * `FILE_DELETE`) can only read files they uploaded themselves,
 * preventing horizontal privilege escalation.
 *
 * Records `file.viewed` track event.
 */
export async function getFileAction(
  id: string
): Promise<ActionState<FileAttachment>> {
  try {
    const ctx = await requirePermission(Permissions.FILE_READ)
    const file = await getFileAttachment(id)
    if (!file) {
      return { success: false, message: "File not found" }
    }

    // Data-level permission: non-admins can only read their own uploads.
    const { allowed: canManage } = await checkPermission(Permissions.FILE_DELETE)
    if (!canManage && file.uploaderId !== ctx.userId) {
      return { success: false, message: "Permission denied" }
    }

    await trackEvent({
      event: "file.viewed",
      userId: ctx.userId,
      targetId: id,
      targetType: "file",
    })

    return { success: true, data: file }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * Delete a single file by ID.
 *
 * Requires `FILE_DELETE` permission. Persists removal via storageProvider
 * abstraction (no direct fs/promises calls). Records `file.deleted` track
 * event + audit log entry.
 */
export async function deleteFileAction(
  id: string
): Promise<ActionState<{ id: string }>> {
  try {
    const ctx = await requirePermission(Permissions.FILE_DELETE)
    const file = await getFileAttachment(id)
    if (!file) {
      return { success: false, message: "File not found" }
    }

    await storageProvider.delete(file.storagePath)

    const ok = await deleteFileAttachment(id)
    if (!ok) {
      return { success: false, message: "Failed to delete file record" }
    }

    await trackEvent({
      event: "file.deleted",
      userId: ctx.userId,
      targetId: id,
      targetType: "file",
      properties: { filename: file.originalName, size: file.size },
    })

    await logAudit({
      action: "delete",
      module: "files",
      targetId: id,
      targetType: "file",
      detail: { filename: file.originalName, mimeType: file.mimeType },
    })

    return { success: true, data: { id } }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * Batch delete files by IDs.
 *
 * Requires `FILE_DELETE` permission. Input is Zod-validated (max 100 ids
 * per call). Persists storage removal via storageProvider abstraction.
 * Records `file.batch_deleted` track event + audit log entry.
 */
export async function batchDeleteFilesAction(
  rawIds: unknown
): Promise<ActionState<BatchDeleteResult>> {
  try {
    const ctx = await requirePermission(Permissions.FILE_DELETE)

    const { ids } = BatchDeleteSchema.parse({ ids: rawIds })

    const files = await getFileAttachmentsByIds(ids)

    await Promise.all(
      files.map((f) =>
        storageProvider.delete(f.storagePath).catch(() => undefined)
      )
    )

    const result = await deleteFileAttachments(ids)

    await trackEvent({
      event: "file.batch_deleted",
      userId: ctx.userId,
      targetType: "file",
      properties: {
        requestedCount: ids.length,
        deletedCount: result.deletedCount,
        failedCount: result.failedIds.length,
      },
    })

    await logAudit({
      action: "batch_delete",
      module: "files",
      targetType: "file",
      detail: {
        requestedCount: ids.length,
        deletedCount: result.deletedCount,
        failedIds: result.failedIds,
      },
    })

    return { success: true, data: result }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * Get file list with filters (admin).
 *
 * Requires `FILE_READ` permission. Input is Zod-validated to enforce
 * limit (1..200) and offset (>=0) bounds.
 */
export async function getFileListAction(
  params?: Partial<FileAttachmentQueryParams>
): Promise<ActionState<{ files: FileAttachment[] }>> {
  try {
    await requirePermission(Permissions.FILE_READ)
    const query = FileListQuerySchema.parse(params ?? {})
    const files = await getFileAttachmentsWithFilters(query)
    return { success: true, data: { files } }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * Get file statistics (admin dashboard).
 *
 * Requires `FILE_READ` permission.
 */
export async function getFileStatsAction(): Promise<ActionState<FileStatsType>> {
  try {
    await requirePermission(Permissions.FILE_READ)
    const stats = await getFileStats()
    return { success: true, data: stats }
  } catch (e) {
    return handleActionError(e)
  }
}
