"use server"

import { revalidatePath } from "next/cache"
import Papa from "papaparse"

import { ImportFormType, importSchema } from "@/lib/schemas/import.schemas"
import { ImportItem } from "@/lib/types"
import { AssetService } from "@/services/asset.service"
import { AssignmentService } from "@/services/assignment.service"
import { CategoryService } from "@/services/category.service"
import { ItemService } from "@/services/item.service"
import { MovementService } from "@/services/movement.service"
import { RecipientService } from "@/services/recipient.service"

export async function importItems(formData: ImportFormType) {
  const validatedFields = importSchema.safeParse(formData)

  if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
    }
  }

  const { file, categoryId } = validatedFields.data

  if (!file) {
    return {
      errors: {
        file: ["File is required"],
      },
    }
  }

  const fileStream = await file.text()

  const parsedFile = Papa.parse<Record<string, string | undefined>>(
    fileStream,
    {
      header: true,
      skipEmptyLines: true,
      dynamicTyping: false,
    },
  )

  const { data: rows, meta, errors: papaErrors } = parsedFile

  if (papaErrors.length > 0) {
    return {
      errors: {
        file: papaErrors.map((err) => err.message).flat(),
      },
    }
  }

  if (!rows || rows.length === 0) {
    return {
      errors: {
        file: ["File is empty"],
      },
    }
  }

  const fileHeaders = meta?.fields || []

  if (fileHeaders.length === 0) {
    return {
      errors: {
        file: ["File has no headers"],
      },
    }
  }

  if (!fileHeaders.includes("name")) {
    return {
      errors: {
        file: ["Required header name is missing"],
      },
    }
  }

  if (categoryId) {
    if (
      fileHeaders.includes("category") ||
      fileHeaders.includes("categoryId")
    ) {
      return {
        errors: {
          file: [
            "If you select a category in the form, you must not select a category or categoryId in the file",
          ],
        },
      }
    } else {
      if (
        !fileHeaders.includes("category") &&
        !fileHeaders.includes("categoryId")
      ) {
        return {
          errors: {
            file: [
              "If you not select a category in the form, you must select a category or categoryId in the file",
            ],
          },
        }
      }
    }
  }

  if (fileHeaders.includes("category") && fileHeaders.includes("categoryId")) {
    return {
      errors: {
        file: [
          "Only one of category or categoryId is allowed, you must select one of them",
        ],
      },
    }
  }

  if (
    fileHeaders.includes("assigned") &&
    !fileHeaders.includes("firstName") &&
    !fileHeaders.includes("lastName")
  ) {
    return {
      errors: {
        file: [
          "If you select assigned, you must select firstName and lastName in the file",
        ],
      },
    }
  }

  const itemsToCreate: ImportItem[] = []
  const importErrors: string[] = []

  for (const [index, row] of rows.entries()) {
    const {
      name,
      stock,
      serialNumber,
      categoryId,
      category,
      deliveryNote,
      assigned,
      username,
      firstName,
      lastName,
    } = row

    if (!name) {
      importErrors.push(`Row ${index + 2}: Name is required`)
    }

    if (!categoryId && !category) {
      importErrors.push(`Row ${index + 2}: Category or categoryId is required`)
    }

    if (stock && isNaN(Number(stock))) {
      importErrors.push(`Row ${index + 2}: Stock must be a number`)
    }

    if (serialNumber && typeof serialNumber !== "string") {
      importErrors.push(`Row ${index + 2}: Serial number must be a string`)
    }

    if (deliveryNote && typeof deliveryNote !== "string") {
      importErrors.push(`Row ${index + 2}: Delivery note must be a string`)
    }

    if (username && typeof username !== "string") {
      importErrors.push(`Row ${index + 2}: Username must be a string`)
    }

    if (firstName && typeof firstName !== "string") {
      importErrors.push(`Row ${index + 2}: First name must be a string`)
    }

    if (lastName && typeof lastName !== "string") {
      importErrors.push(`Row ${index + 2}: Last name must be a string`)
    }

    if (assigned === "true" && !firstName && !lastName) {
      importErrors.push(
        `Row ${index + 2}: If assigned is true, firstName and lastName are required`,
      )
    }
  }

  if (importErrors.length > 0) {
    return {
      errors: {
        file: importErrors,
      },
    }
  }

  for (const row of rows) {
    itemsToCreate.push({
      name: row.name?.trim() || "",
      stock: row.stock ? Number(row.stock) : row.serialNumber ? 1 : 0,
      serialNumber: row.serialNumber?.trim() || "",
      categoryId: categoryId ? categoryId : row.categoryId?.trim() || "",
      category: row.category?.trim() || "",
      deliveryNote: row.deliveryNote?.trim() || "",
      assigned: row.assigned?.trim() === "true" ? true : false,
      username: row.username?.trim() || "",
      firstName: row.firstName?.trim() || "",
      lastName: row.lastName?.trim() || "",
    })
  }

  for (const item of itemsToCreate) {
    const {
      name,
      stock,
      serialNumber,
      categoryId,
      category,
      deliveryNote,
      assigned,
      username,
      firstName,
      lastName,
    } = item

    // Reset variables at the beginning of each iteration
    let newItem
    let newAsset
    let newCategory
    let newRecipient
    let newAssignment

    const existingCategory = categoryId
      ? await CategoryService.findById(categoryId)
      : await CategoryService.findByName(category || "")

    if (!existingCategory && category) {
      newCategory = await CategoryService.create({ name: category })
    } else {
      newCategory = existingCategory
    }

    const existingItem = await ItemService.findByName(name)

    if (!existingItem) {
      newItem = await ItemService.create({
        name,
        stock: assigned ? 0 : stock || 0,
        category: {
          connect: { id: categoryId ? categoryId : newCategory?.id || "" },
        },
      })
    } else {
      newItem = existingItem
      await ItemService.update(existingItem.id, {
        name: existingItem.name,
        stock: !assigned
          ? (existingItem?.stock || 0) + (stock || 1)
          : existingItem?.stock || 0,
      })
    }

    if (serialNumber) {
      const existingAsset = await AssetService.findBySerialNumber(serialNumber)

      if (!existingAsset) {
        newAsset = await AssetService.create({
          item: {
            connect: { id: newItem.id },
          },
          serialNumber,
          status: assigned ? "ASSIGNED" : "AVAILABLE",
          deliveryNote: deliveryNote || "",
        })
      } else {
        // Skip asset if already exists
        continue
      }
    }

    if (assigned && firstName && lastName) {
      const finalUsername =
        username || `${firstName.toLowerCase()[0]}${lastName.toLowerCase()}`
      const existingRecipient =
        await RecipientService.findByUsername(finalUsername)

      if (!existingRecipient) {
        newRecipient = await RecipientService.create({
          username: finalUsername,
          firstName,
          lastName,
          email: undefined,
          phone: "",
          department: "OTHER",
        })
      } else {
        newRecipient = existingRecipient
      }

      newAssignment = await AssignmentService.create({
        quantity: stock || 1,
        notes: deliveryNote || "",
        itemId: newItem?.id || "",
        assetId: newAsset?.id || "",
        recipientId: newRecipient?.id || "",
        assignmentDate: new Date(),
      })
    }

    const movementData: any = {
      assetId: newAsset?.id || undefined,
      quantity: stock || 1,
      type: assigned ? "ASSIGNMENT" : "IN",
      itemId: newItem?.id || undefined,
      recipientId: newRecipient?.id || undefined,
    }

    if (newAssignment?.id) {
      movementData.assignmentId = newAssignment.id
    }

    if (newRecipient?.id) {
      movementData.recipientId = newRecipient.id
    }

    await MovementService.create(movementData)
  }

  revalidatePath("/inventory/items")

  return {
    success: true,
    message: "Items imported successfully!",
  }
}
