import { NextResponse } from "next/server"
import { and, desc, eq } from "drizzle-orm"
import { db } from "@/lib/db"
import { documents, properties } from "@/lib/db/schema"
import { getSessionUser } from "@/lib/session"
import {
  saveFile,
  isAllowedUploadExt,
  StorageNotConfiguredError,
  keyBelongsToOwner,
  contentMatchesExtension,
  extOf,
} from "@/lib/storage"
import { checkStorageLimit } from "@/lib/plan-limits"
import { ownsProperty, ownsTenant } from "@/lib/db/ownership"
import { getEffectiveOwnerId, getAccountContext } from "@/lib/account"

export async function GET(request: Request) {
  const user = await getSessionUser()
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })

  const ownerId = await getEffectiveOwnerId(user.id)

  const { searchParams } = new URL(request.url)
  const propertyId = searchParams.get("property_id")

  let propertyName = ""
  if (propertyId) {
    const prop = await db.query.properties.findFirst({
      where: and(eq(properties.id, propertyId), eq(properties.user_id, ownerId)),
      columns: { name: true },
    })
    propertyName = prop?.name ?? ""
  }

  const data = await db.query.documents.findMany({
    where: and(
      eq(documents.user_id, ownerId),
      propertyId ? eq(documents.property_id, propertyId) : undefined
    ),
    orderBy: desc(documents.created_at),
  })

  return NextResponse.json({ documents: data, propertyName })
}

export async function POST(request: Request) {
  const user = await getSessionUser()
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })

  const ctx = await getAccountContext(user.id)
  const ownerId = ctx.ownerId
  if (!ctx.canWrite) return NextResponse.json({ error: "Forbidden" }, { status: 403 })

  const contentType = request.headers.get("content-type") ?? ""

  if (contentType.includes("multipart/form-data")) {
    const fd = await request.formData()
    const file = fd.get("file") as File | null
    const propertyId = fd.get("property_id") as string
    const name = fd.get("name") as string
    const category = ((fd.get("category") as string) || "other") as typeof documents.$inferInsert.category

    if (!file) return NextResponse.json({ error: "No file provided" }, { status: 400 })
    if (file.size > 20 * 1024 * 1024) return NextResponse.json({ error: "File too large (max 20 MB)" }, { status: 400 })
    if (!isAllowedUploadExt(file.name)) return NextResponse.json({ error: "File type not allowed" }, { status: 400 })
    const head = Buffer.from(await file.slice(0, 16).arrayBuffer())
    if (!contentMatchesExtension(head, extOf(file.name))) {
      return NextResponse.json({ error: "File content does not match its type" }, { status: 400 })
    }

    const storageError = await checkStorageLimit(ownerId, file.size)
    if (storageError) return NextResponse.json({ error: storageError }, { status: 403 })

    // Verify the property belongs to the user before attaching a document to it.
    const prop = await db.query.properties.findFirst({
      where: and(eq(properties.id, propertyId), eq(properties.user_id, ownerId)),
      columns: { id: true },
    })
    if (!prop) return NextResponse.json({ error: "Property not found" }, { status: 404 })

    let saved
    try {
      saved = await saveFile(file, { userId: ownerId, scope: "documents" })
    } catch (err) {
      if (err instanceof StorageNotConfiguredError) {
        console.error("[documents]", err.message)
        return NextResponse.json(
          { error: "File uploads are temporarily unavailable. Please try again later." },
          { status: 503 }
        )
      }
      throw err
    }
    const { key, size, type } = saved

    const [data] = await db
      .insert(documents)
      .values({
        user_id: ownerId,
        property_id: propertyId,
        name: name || file.name,
        category,
        file_url: `/api/files/${key}`,
        storage_path: key,
        file_type: type,
        file_size: size,
      })
      .returning()

    return NextResponse.json(data, { status: 201 })
  }

  // JSON fallback (metadata only)
  const body = (await request.json()) as Record<string, unknown>
  const propertyId = body.property_id as string | undefined
  const tenantId = body.tenant_id as string | undefined

  // Verify the property/tenant belong to the user before attaching a document.
  if (!(await ownsProperty(ownerId, propertyId))) {
    return NextResponse.json({ error: "Property not found" }, { status: 404 })
  }
  if (!(await ownsTenant(ownerId, tenantId))) {
    return NextResponse.json({ error: "Tenant not found" }, { status: 404 })
  }

  // The file reference is client-supplied. Require it to be an /api/files URL
  // inside the caller's OWN namespace, and derive storage_path from it — never
  // trust a separate client storage_path (which could point at another tenant's
  // object and later be deleted). Also blocks javascript:/external file_url values.
  const FILES_PREFIX = "/api/files/"
  const fileUrl = typeof body.file_url === "string" ? body.file_url : ""
  if (!fileUrl.startsWith(FILES_PREFIX)) {
    return NextResponse.json({ error: "file_url must reference an uploaded file" }, { status: 400 })
  }
  const storagePath = fileUrl.slice(FILES_PREFIX.length)
  if (!keyBelongsToOwner(storagePath, ownerId)) {
    return NextResponse.json({ error: "Invalid file reference" }, { status: 403 })
  }

  // Whitelist insertable columns — never trust client-supplied user_id/id/created_at.
  const [data] = await db
    .insert(documents)
    .values({
      user_id: ownerId,
      property_id: propertyId as string,
      tenant_id: tenantId,
      name: body.name as string,
      category: (body.category as typeof documents.$inferInsert.category) ?? "other",
      file_url: fileUrl,
      storage_path: storagePath,
      file_type: body.file_type as string | undefined,
      file_size: body.file_size as number | undefined,
    })
    .returning()

  return NextResponse.json(data, { status: 201 })
}
