import { Credential } from "@/common/types/auth";
import { StockOpnameLocationRes, StockOpnameRequest } from "@/common/types/stockOpname";
import { Team } from "@prisma/client";
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "prisma/client";

type Quantity = {
  [key in keyof typeof Team]: number | null
}

const calculateOpnameQuantity = async (
  where: {
    productId: number,
    companyId: number
  }
): Promise<Quantity> => {
  const quantity: Quantity = {
    COUNT1: null,
    COUNT2: null,
    VERIFICATION: null,
  }
  for (const team of Object.values(Team)) {
    const opnameQty = await prisma.stockOpname.groupBy({
      by: ['productId', 'team'],
      _sum: { quantity: true },
      where: { team, ...where }
    })
    if (opnameQty.length === 0) continue
    quantity[team] = opnameQty[0]._sum.quantity
  }
  return quantity
}

export async function GET(request: NextRequest) {
  const PAGE_SIZE = 30;

  const params = request.nextUrl.searchParams
  const companyId = params.get('companyId')
  const search = params.get('search')
  const page = params.get('page') ?? null;
  const intPage = page ? parseInt(page) : 1;

  if (!companyId) {
    return NextResponse.json({ error: 'Bad Request. Missing companyId' }, { status: 400 })
  }

  const where = {
    AND: {
      stockOpnames: { some: {} },
      companyId: parseInt(companyId),
      OR: [
        { name: { contains: search ?? '' } },
        { itemCode: { contains: search ?? '' } },
        { barcode: { contains: search ?? '' } },
      ]
    }
  }

  const products = await prisma.product.findMany({
    skip: (intPage - 1) * PAGE_SIZE,
    take: PAGE_SIZE,
    where,
    select: {
      id: true,
      name: true,
      itemCode: true,
      barcode: true,
      onhandQty: true,
      differenceQty: true,
      isDifferent: true
    }
  })

  const productCount = await prisma.product.count({ where })

  const pagination = {
    page: intPage,
    totalPage: Math.ceil(productCount / PAGE_SIZE),
  }

  type ProductWithSum = typeof products[0] & { quantity: Quantity }

  const productsWithSum: ProductWithSum[] = []

  for (const product of products) {
    const quantity = await calculateOpnameQuantity({
      productId: product.id,
      companyId: parseInt(companyId)
    })
    productsWithSum.push({ ...product, quantity })
  }

  return NextResponse.json({
    result: productsWithSum,
    ...pagination
  })
}

export async function POST(request: NextRequest) {
  const credentialStr = request.cookies.get('credential')?.value
  const credential: Credential | null = credentialStr ? JSON.parse(credentialStr) : null

  if (!credential) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })

  const body: StockOpnameRequest = await request.json()

  const { companyId, team } = credential

  const query = {
    locationId: body.location,
    productId: body.product,
    companyId,
    team
  }

  const stockOpname = await prisma.stockOpname.findFirst({
    where: query
  })

  const data = {
    ...query,
    userId: credential.id,
    quantity: body.quantity,
    isDifferent: false
  }

  let newStockOpname = null

  if (!stockOpname) {
    newStockOpname = await prisma.stockOpname.create({ data })
  } else {
    newStockOpname = await prisma.stockOpname.update({
      where: { id: stockOpname.id },
      data
    })
  }

  computeIsDifferent({ productId: body.product, companyId: companyId })

  return NextResponse.json(newStockOpname)
}

const SELF_HOST = process.env.SELF_HOST as string

const computeIsDifferent = async ({
  companyId,
  productId
}: {
  companyId: number,
  productId: number
}) => {
  const totalQty: { [key in keyof typeof Team]: number | null } = {
    COUNT1: null,
    COUNT2: null,
    VERIFICATION: null
  }

  const searchParams = new URLSearchParams({
    companyId: companyId.toString(),
    productId: productId.toString()
  })

  const stockOpnamesFetch = await fetch(`${SELF_HOST}/api/stock-opname/location?${searchParams}`)
  const stockOpnames: StockOpnameLocationRes[] = await stockOpnamesFetch.json()

  const count2Count = await prisma.stockOpname.count({
    where: { companyId, productId, team: 'COUNT2' }
  })

  const isCount2Counted: boolean = count2Count > 0

  let isDifferent: boolean = false

  for (const opname of stockOpnames) {
    let { COUNT1, COUNT2, VERIFICATION } = opname

    if (!totalQty['COUNT1'] && COUNT1.quantity) totalQty['COUNT1'] = 0
    if (!totalQty['COUNT2'] && COUNT2.quantity) totalQty['COUNT2'] = 0
    if (!totalQty['VERIFICATION'] && VERIFICATION.quantity) totalQty['VERIFICATION'] = 0

    if (totalQty['COUNT1'] !== null && COUNT1.quantity) totalQty['COUNT1'] += COUNT1.quantity
    if (totalQty['COUNT2'] !== null && COUNT2.quantity) totalQty['COUNT2'] += COUNT2.quantity
    if (totalQty['VERIFICATION'] !== null && VERIFICATION.quantity) totalQty['VERIFICATION'] += VERIFICATION.quantity

    if (isCount2Counted && COUNT1.quantity != COUNT2.quantity) {
      isDifferent = true
    }
  }

  const product = await prisma.product.findFirst({
    where: { id: productId }
  })
  if (!product) return

  const onhandQty = product?.onhandQty || 0

  if (!isDifferent) {
    if (
      (typeof totalQty['VERIFICATION'] === 'number' && totalQty['VERIFICATION'] > 0) ||
      totalQty['COUNT2'] == onhandQty ||
      totalQty['COUNT1'] == onhandQty ||
      totalQty['COUNT1'] == totalQty['COUNT2']
    ) {
      isDifferent = false
    } else {
      isDifferent = true
    }

    if (isCount2Counted && totalQty['COUNT1'] != onhandQty) {
      isDifferent = true
    }
  }

  await prisma.product.update({
    where: { id: product.id },
    data: { isDifferent }
  })
}