import { Prisma } from '@prisma/client'
import { pageArgs, prismaClient } from './prisma.mjs'
import { createUploadedFileRecord } from './uploaded-files-repository.mjs'

const activeInvoiceStatuses = new Set(['pending', 'processing', 'issued', 'failed'])

function isUuid(value) {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(String(value || ''))
}

async function loadFiles(invoiceItems) {
  const prisma = prismaClient()
  const ids = [...new Set(invoiceItems.map((item) => item.pdfFileId).filter(Boolean))]
  if (!ids.length) return new Map()
  const files = await prisma.uploadedFile.findMany({
    where: { id: { in: ids } },
    select: { id: true, fileName: true },
  })
  return new Map(files.map((file) => [file.id, file]))
}

function invoiceJson(item, file = null) {
  if (!item) return null
  const order = item.order || {}
  const user = order.user || {}
  return {
    id: item.id,
    invoiceNo: item.invoiceNo || '',
    invoiceType: item.invoiceType || 'normal_e_invoice',
    invoiceTypeLabel: '普通电子发票',
    orderId: item.orderId,
    orderNo: order.orderNo || '',
    settlementNo: order.settlementNo || '',
    userId: order.userId || item.applicantUserId || '',
    userPhone: user.phone || '',
    title: item.title || '',
    taxNumber: item.taxNumber || '',
    email: item.email || '',
    amount: item.amount || 0,
    status: item.status || 'pending',
    provider: item.provider || 'manual',
    providerInvoiceId: item.providerInvoiceId || '',
    pdfFileId: item.pdfFileId || null,
    fileName: file?.fileName || '',
    fileUrl: item.pdfFileId ? `/api/files/${item.pdfFileId}` : '',
    failureReason: item.failureReason || '',
    remark: item.remark || '',
    adminNote: item.adminNote || '',
    issuedBy: item.issuedBy || '',
    issuedAt: item.issuedAt,
    createdAt: item.createdAt,
    updatedAt: item.updatedAt,
    fileUploadedAt: item.fileUploadedAt,
  }
}

function invoiceSelect() {
  return {
    id: true,
    invoiceNo: true,
    invoiceType: true,
    orderId: true,
    applicantUserId: true,
    title: true,
    taxNumber: true,
    email: true,
    amount: true,
    status: true,
    provider: true,
    providerInvoiceId: true,
    pdfFileId: true,
    failureReason: true,
    remark: true,
    adminNote: true,
    issuedBy: true,
    issuedAt: true,
    fileUploadedAt: true,
    createdAt: true,
    updatedAt: true,
    order: {
      select: {
        id: true,
        orderNo: true,
        settlementNo: true,
        userId: true,
        user: {
          select: {
            id: true,
            phone: true,
            displayName: true,
          },
        },
      },
    },
  }
}

function invoiceWhereByKey(key) {
  const value = String(key || '').trim()
  return isUuid(value) ? { id: value } : { invoiceNo: value }
}

export async function findUserOrderForInvoice(userId, orderKey) {
  const prisma = prismaClient()
  const value = String(orderKey || '').trim()
  if (!value) return null
  const order = await prisma.order.findFirst({
    where: {
      userId,
      OR: [
        ...(isUuid(value) ? [{ id: value }] : []),
        { orderNo: value },
        { legacyOrderNo: value },
        { settlementNo: value },
      ],
    },
    select: {
      id: true,
      orderNo: true,
      status: true,
      totalAmount: true,
      paidAmount: true,
    },
  })
  if (!order) return null
  return {
    id: order.id,
    orderNo: order.orderNo,
    status: order.status,
    totalAmount: order.totalAmount,
    paidAmount: order.paidAmount,
    amount: Number(order.paidAmount || order.totalAmount || 0),
  }
}

export async function findInvoiceApplicationByKey(key, { userId = '' } = {}) {
  const prisma = prismaClient()
  const item = await prisma.invoiceApplication.findFirst({
    where: {
      ...invoiceWhereByKey(key),
      ...(userId ? { order: { is: { userId } } } : {}),
    },
    select: invoiceSelect(),
  })
  if (!item) return null
  const files = await loadFiles([item])
  return invoiceJson(item, files.get(item.pdfFileId))
}

export async function createInvoiceApplication({ invoiceNo, userId, order, invoice }) {
  const prisma = prismaClient()
  const duplicate = await prisma.invoiceApplication.findFirst({
    where: {
      orderId: order.id,
      status: { in: [...activeInvoiceStatuses] },
    },
    select: { invoiceNo: true, status: true },
    orderBy: { createdAt: 'desc' },
  })
  if (duplicate) return { duplicate }

  await prisma.invoiceApplication.create({
    data: {
      invoiceNo,
      invoiceType: 'normal_e_invoice',
      orderId: order.id,
      applicantUserId: userId,
      title: invoice.title,
      taxNumber: invoice.taxNumber,
      email: invoice.email,
      amount: order.amount,
      status: 'pending',
      provider: 'manual',
      remark: invoice.remark,
    },
  })
  return {
    item: await findInvoiceApplicationByKey(invoiceNo, { userId }),
  }
}

export async function listUserInvoiceApplications(userId, filters = {}) {
  const prisma = prismaClient()
  const status = String(filters.status || '').trim()
  const items = await prisma.invoiceApplication.findMany({
    where: {
      order: { is: { userId } },
      ...(status ? { status } : {}),
    },
    select: invoiceSelect(),
    orderBy: { createdAt: 'desc' },
  })
  const files = await loadFiles(items)
  return items.map((item) => invoiceJson(item, files.get(item.pdfFileId)))
}

export async function listInvoiceApplications(filters = {}) {
  const prisma = prismaClient()
  const status = String(filters.status || '').trim()
  const q = String(filters.q || filters.search || '').trim()
  const from = String(filters.from || '').trim()
  const to = String(filters.to || '').trim()
  const { page, pageSize, skip, take } = pageArgs({
    page: filters.page || 1,
    pageSize: filters.pageSize || 50,
  })
  const fromDate = from ? new Date(`${from}T00:00:00.000+08:00`) : null
  const toDate = to ? new Date(`${to}T00:00:00.000+08:00`) : null
  const createdAt = {
    ...(fromDate && Number.isFinite(fromDate.getTime()) ? { gte: fromDate } : {}),
    ...(toDate && Number.isFinite(toDate.getTime()) ? { lt: new Date(toDate.getTime() + 24 * 60 * 60 * 1000) } : {}),
  }
  const where = {
    ...(status ? { status } : {}),
    ...(Object.keys(createdAt).length ? { createdAt } : {}),
    ...(q ? {
      OR: [
        { invoiceNo: { contains: q, mode: 'insensitive' } },
        { title: { contains: q, mode: 'insensitive' } },
        { taxNumber: { contains: q, mode: 'insensitive' } },
        { email: { contains: q, mode: 'insensitive' } },
        { order: { is: { orderNo: { contains: q, mode: 'insensitive' } } } },
        { order: { is: { settlementNo: { contains: q, mode: 'insensitive' } } } },
        { order: { is: { user: { is: { phone: { contains: q, mode: 'insensitive' } } } } } },
      ],
    } : {}),
  }
  const [items, total] = await Promise.all([
    prisma.invoiceApplication.findMany({
      where,
      select: invoiceSelect(),
      orderBy: { createdAt: 'desc' },
      skip,
      take,
    }),
    prisma.invoiceApplication.count({ where }),
  ])
  const files = await loadFiles(items)
  return {
    items: items.map((item) => invoiceJson(item, files.get(item.pdfFileId))),
    page,
    pageSize,
    total,
  }
}

export async function listInvoiceApplicationRows(filters = {}) {
  const prisma = prismaClient()
  const status = String(filters.status || '').trim()
  const q = String(filters.q || filters.search || '').trim()
  const from = String(filters.from || '').trim()
  const to = String(filters.to || '').trim()
  const conditions = []
  if (status) conditions.push(Prisma.sql`ia.status = ${status}`)
  if (from) conditions.push(Prisma.sql`ia.created_at >= ${from}::date`)
  if (to) conditions.push(Prisma.sql`ia.created_at < (${to}::date + interval '1 day')`)
  if (q) {
    const like = `%${q}%`
    conditions.push(Prisma.sql`(
      ia.invoice_no ilike ${like}
      or ia.title ilike ${like}
      or o.order_no ilike ${like}
      or o.settlement_no ilike ${like}
      or u.phone ilike ${like}
    )`)
  }
  const where = conditions.length ? Prisma.sql`where ${Prisma.join(conditions, ' and ')}` : Prisma.empty
  return prisma.$queryRaw(Prisma.sql`
    select
      ia.invoice_no as "申请单号",
      o.order_no as "订单号",
      o.settlement_no as "结算编号",
      u.phone as "手机号",
      ia.amount / 100.0 as "金额",
      ia.title as "发票抬头",
      coalesce(ia.tax_number, '') as "税号",
      ia.email as "接收邮箱",
      ia.status as "状态",
      coalesce(ia.provider_invoice_id, '') as "发票号码",
      ia.created_at as "申请时间",
      ia.issued_at as "开票时间",
      coalesce(ia.failure_reason, '') as "原因"
    from invoice_applications ia
    join orders o on o.id = ia.order_id
    join users u on u.id = o.user_id
    ${where}
    order by ia.created_at desc
  `)
}

export async function updateInvoiceApplicationRecord(current, next = {}) {
  const prisma = prismaClient()
  await prisma.invoiceApplication.update({
    where: { id: current.id },
    data: {
      status: next.status,
      providerInvoiceId: next.providerInvoiceId,
      failureReason: next.failureReason,
      adminNote: next.adminNote,
      issuedBy: next.issuedBy,
      issuedAt: next.issuedAt ? new Date(next.issuedAt) : undefined,
      updatedAt: new Date(),
    },
  })
  return findInvoiceApplicationByKey(current.invoiceNo || current.id)
}

export async function attachInvoiceFileRecord(current, file) {
  const prisma = prismaClient()
  const uploaded = await createUploadedFileRecord(file, {
    ownerType: 'invoice_application',
    ownerId: current.id,
    visibility: 'private',
  })
  await prisma.invoiceApplication.update({
    where: { id: current.id },
    data: {
      pdfFileId: uploaded.id,
      fileUploadedAt: new Date(),
      updatedAt: new Date(),
    },
  })
  return {
    invoice: await findInvoiceApplicationByKey(current.invoiceNo || current.id),
    file: uploaded,
  }
}
