﻿import { randomUUID } from "crypto"
import { NextResponse } from "next/server"
import type { User_role, User_status } from "@prisma/client"
import { getCurrentUser, isAllowedRole, type CurrentUser } from "@/lib/user"
import { prisma } from "@/lib/prisma"
import { hashPassword } from "@/lib/bcrypt"
import { uploadImage, deleteImage } from "@/lib/r2-storage"
import { MEDIA_FILE_BASE } from "@/lib/media-manager/file-names"
import {
  buildContentAudit,
  contentAuditCreateData,
  contentAuditUserInclude,
  contentAuditUpdateData,
} from "@/lib/content-audit"

export const dynamic = "force-dynamic"
export const revalidate = 0

const NO_CACHE_HEADERS = {
  "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
  Pragma: "no-cache",
  Expires: "0",
}

const MAX_AVATAR_SIZE_BYTES = 1 * 1024 * 1024

const SAFE_SELECT = {
  id: true,
  name: true,
  email: true,
  role: true,
  createdAt: true,
  updatedAt: true,
  status: true,
  image: true,
  imagePublicId: true,
} as const

const USER_LIST_SELECT = {
  ...SAFE_SELECT,
  ...contentAuditUserInclude,
} as const

type UserListRow = {
  id: string
  name: string | null
  email: string
  role: string
  createdAt: Date
  updatedAt: Date
  status: string
  image: string | null
  imagePublicId: string | null
  createdBy?: { id: string; name: string | null; email: string; image: string | null } | null
  updatedBy?: { id: string; name: string | null; email: string; image: string | null } | null
}

function toAdminUserResponse(row: UserListRow) {
  return {
    id: row.id,
    name: row.name,
    email: row.email,
    role: row.role,
    status: row.status,
    image: row.image,
    imagePublicId: row.imagePublicId,
    audit: buildContentAudit(row),
  }
}

type AdminAuthResult =
  | { ok: false; res: NextResponse }
  | { ok: true; user: CurrentUser }

function parseUserRole(value: unknown): User_role | null {
  if (value === "admin" || value === "employee") return value
  return null
}

function parseUserStatus(value: unknown): User_status {
  return value === "inactive" ? "inactive" : "active"
}

function isRemoveImageFlag(value: FormDataEntryValue | null): boolean {
  if (value === null) return false
  const raw = typeof value === "string" ? value.trim().toLowerCase() : String(value).toLowerCase()
  return raw === "true" || raw === "1"
}

async function ensureAdmin(): Promise<AdminAuthResult> {
  const user = await getCurrentUser()
  // Log minimal current-user info for diagnostics (safe to print)
  try {
    console.info("[auth] current user:", { id: user?.id, email: user?.email, role: user?.role })
  } catch (e) {
    // ignore logging errors
  }

  if (!user) {
    return { ok: false, res: NextResponse.json({ error: "Unauthorized" }, { status: 401 }) }
  }
  if (!isAllowedRole(user.role, ["admin"])) {
    return { ok: false, res: NextResponse.json({ error: "Forbidden" }, { status: 403 }) }
  }
  return { ok: true, user }
}

function isSchemaDriftError(err: unknown): boolean {
  if (err && typeof err === "object" && "code" in err && (err as { code?: string }).code === "P2022") {
    return true
  }
  const message = err instanceof Error ? err.message : ""
  return /unknown column|createdbyid|updatedbyid/i.test(message)
}

export async function GET() {
  const auth = await ensureAdmin()
  if (!auth.ok) return auth.res

  try {
    let users: UserListRow[] = []
    try {
      users = await prisma.user.findMany({
        select: USER_LIST_SELECT,
        orderBy: { updatedAt: "desc" },
      }) as unknown as UserListRow[]
    } catch (err) {
      if (!isSchemaDriftError(err)) {
        console.warn("[admin/users] primary query failed, trying compatibility fallback:", err)
      }
      users = await prisma.user.findMany({
        select: SAFE_SELECT,
        orderBy: { updatedAt: "desc" },
      }) as unknown as UserListRow[]
    }

    return NextResponse.json(
      { users: users.map((row) => toAdminUserResponse(row)) },
      { headers: NO_CACHE_HEADERS }
    )
  } catch (error) {
    console.error("/api/admin/users GET error:", error)
    return NextResponse.json({ error: "Server error" }, { status: 500 })
  }
}

export async function POST(req: Request) {
  const auth = await ensureAdmin()
  if (!auth.ok) return auth.res

  try {
    let name: string | undefined
    let email: string | undefined
    let role: string | undefined
    let password: string | undefined
    let status: string | undefined
    let avatarFile: Blob | null = null

    const contentType = req.headers.get("content-type") || ""
    if (contentType.includes("multipart/form-data")) {
      const form = await req.formData()
      name = form.get("name") as string | undefined
      email = form.get("email") as string | undefined
      role = form.get("role") as string | undefined
      password = form.get("password") as string | undefined
      status = form.get("status") as string | undefined
      const file = form.get("image") as unknown
      if (file && file instanceof Blob) {
        if (!file.type.startsWith("image/")) {
          return NextResponse.json({ error: "Only image files are allowed" }, { status: 400 })
        }
        if (file.size > MAX_AVATAR_SIZE_BYTES) {
          return NextResponse.json({ error: "Avatar size must be 1 MB or less" }, { status: 400 })
        }
        avatarFile = file
      }
    } else {
      const body = await req.json()
      name = body.name
      email = body.email
      role = body.role
      password = body.password
      status = body.status
    }

    if (!email || !role || !password) {
      return NextResponse.json({ error: "Missing required fields" }, { status: 400 })
    }

    const normalizedEmail = String(email).toLowerCase()

    const existing = await prisma.user.findUnique({ where: { email: normalizedEmail } })
    if (existing) {
      return NextResponse.json({ error: "Email already in use" }, { status: 400 })
    }

    const roleValue = parseUserRole(role)
    if (!roleValue) {
      return NextResponse.json({ error: "Invalid role" }, { status: 400 })
    }

    const hashed = await hashPassword(String(password))

    const created = await prisma.user.create({
      data: {
        id: randomUUID(),
        name: name ?? null,
        email: normalizedEmail,
        password: hashed,
        role: roleValue,
        status: parseUserStatus(status),
        image: null,
        imagePublicId: null,
        ...contentAuditCreateData(auth.user.id),
      },
      select: USER_LIST_SELECT,
    })

    if (avatarFile) {
      try {
        const buffer = await avatarFile.arrayBuffer()
        const base64 = Buffer.from(buffer).toString("base64")
        const dataUri = `data:${avatarFile.type};base64,${base64}`
        const uploaded = await uploadImage(dataUri, {
          prefix: `users/${created.id}/avatar`,
          fileBaseName: MEDIA_FILE_BASE.userAvatar,
        })
        const withImage = await prisma.user.update({
          where: { id: created.id },
          data: {
            image: uploaded.url,
            imagePublicId: uploaded.mediaKey,
            ...contentAuditUpdateData(auth.user.id),
          },
          select: USER_LIST_SELECT,
        })
        return NextResponse.json({ user: toAdminUserResponse(withImage) }, { status: 201 })
      } catch {
        await prisma.user.delete({ where: { id: created.id } }).catch(() => {})
        return NextResponse.json({ error: "Avatar upload failed" }, { status: 500 })
      }
    }

    return NextResponse.json({ user: toAdminUserResponse(created) }, { status: 201 })
  } catch (error) {
    return NextResponse.json({ error: "Server error" }, { status: 500 })
  }
}

export async function PATCH(req: Request) {
  const auth = await ensureAdmin()
  if (!auth.ok) return auth.res

  try {
    const contentType = req.headers.get("content-type") || ""
    let id: string | undefined
    let name: string | undefined
    let role: string | undefined
    let password: string | undefined
    let status: string | undefined
    let newImageUrl: string | undefined
    let newImagePublicId: string | undefined
    let removeImageFlag = false
    // Parse JSON body once if not multipart
    const parsedBody = contentType.includes("multipart/form-data") ? null : await req.json().catch(() => null)

    if (contentType.includes("multipart/form-data")) {
      const form = await req.formData()
      id = form.get("id") as string | undefined
      name = form.get("name") as string | undefined
      role = form.get("role") as string | undefined
      password = form.get("password") as string | undefined
      status = form.get("status") as string | undefined
      const removeField = form.get("removeImage")
      if (isRemoveImageFlag(removeField)) {
        removeImageFlag = true
      }
      const file = form.get("image") as any
      if (file && file instanceof Blob) {
        if (!file.type.startsWith("image/")) {
          return NextResponse.json({ error: "Only image files are allowed" }, { status: 400 })
        }
        if (file.size > MAX_AVATAR_SIZE_BYTES) {
          return NextResponse.json({ error: "Avatar size must be 1 MB or less" }, { status: 400 })
        }
        const buffer = await file.arrayBuffer()
        const base64 = Buffer.from(buffer).toString("base64")
        const dataUri = `data:${file.type};base64,${base64}`
        const uploaded = await uploadImage(dataUri, {
          prefix: `users/${id}/avatar`,
          fileBaseName: MEDIA_FILE_BASE.userAvatar,
        })
        newImageUrl = uploaded.url
        newImagePublicId = uploaded.mediaKey
      }
    } else if (parsedBody) {
      id = parsedBody.id
      name = parsedBody.name
      role = parsedBody.role
      password = parsedBody.password
      status = parsedBody.status
      if ((parsedBody as any).removeImage) removeImageFlag = true
    }

    if (!id) return NextResponse.json({ error: "Missing user id" }, { status: 400 })

    const data: Record<string, unknown> = {}
    if (name !== undefined) data.name = name
    if (role !== undefined) {
      const roleValue = parseUserRole(role)
      if (!roleValue) return NextResponse.json({ error: "Invalid role" }, { status: 400 })
      data.role = roleValue
    }
    if (status !== undefined) data.status = parseUserStatus(status)
    if (password) {
      data.password = await hashPassword(String(password))
    }

    // If new image uploaded, delete old one and set new
    // If explicitly removing image (removeImage true in JSON), delete old and null fields
    // If removeImage requested via JSON body or multipart form field, delete existing
    if (removeImageFlag) {
      console.info("/api/admin/users PATCH: removeImage requested for id:", id)
    }

    if (removeImageFlag) {
      const existing = await prisma.user.findUnique({ where: { id }, select: { imagePublicId: true } })
      if (existing?.imagePublicId) {
        console.info("/api/admin/users PATCH: deleting R2 media key:", existing.imagePublicId)
        await deleteImage(existing.imagePublicId)
        console.info("/api/admin/users PATCH: R2 delete attempted")
      }
      data.image = null
      data.imagePublicId = null
    } else if (newImageUrl) {
      const existing = await prisma.user.findUnique({ where: { id }, select: { imagePublicId: true } })
      if (existing?.imagePublicId) await deleteImage(existing.imagePublicId)
      data.image = newImageUrl
      data.imagePublicId = newImagePublicId
    }

    const updated = await prisma.user.update({
      where: { id },
      data: {
        ...data,
        ...contentAuditUpdateData(auth.user.id),
      },
      select: USER_LIST_SELECT,
    })

    return NextResponse.json({ user: toAdminUserResponse(updated) })
  } catch (error) {
    return NextResponse.json({ error: "Server error" }, { status: 500 })
  }
}

export async function DELETE(req: Request) {
  const auth = await ensureAdmin()
  if (!auth.ok) return auth.res

  try {
    const body = await req.json()
    const { id } = body

    if (!id) return NextResponse.json({ error: "Missing user id" }, { status: 400 })

    // Prevent deleting yourself
    if (auth.user.id === id) {
      return NextResponse.json({ error: "Cannot delete yourself" }, { status: 400 })
    }

    // Delete R2 image if present
    const existing = await prisma.user.findUnique({ where: { id }, select: { imagePublicId: true } })
    if (existing?.imagePublicId) await deleteImage(existing.imagePublicId)

    const deleted = await prisma.user.delete({ where: { id }, select: USER_LIST_SELECT })

    return NextResponse.json({ user: toAdminUserResponse(deleted) })
  } catch (error) {
    return NextResponse.json({ error: "Server error" }, { status: 500 })
  }
}
