import { randomUUID } from "crypto"
import 'dotenv/config'
import { TransactionStatus } from 'generated/prisma/enums'
import { PrismaService } from "src/prisma/prisma.service"

const prisma = new PrismaService()

/* ------------------ helpers ------------------ */

function randomBetween(min: number, max: number) {
  return Math.random() * (max - min) + min
}

function randomInt(min: number, max: number) {
  return Math.floor(randomBetween(min, max + 1))
}

function pick<T>(arr: T[]): T {
  return arr[Math.floor(Math.random() * arr.length)]
}

/* ------------------ customer helpers ------------------ */

const firstNames = [
  "Agus", "Budi", "Made", "Putu", "Ketut", "Wayan", "Sari", "Dewi", "Putri", "Ayu",
  "Rudi", "Joko", "Indra", "Rina", "Dian", "Lina", "Bagus", "Tari", "Gita", "Nanda"
]

const lastNames = [
  "Saputra", "Wijaya", "Santoso", "Pratama", "Utama",
  "Mahendra", "Putra", "Suryadi", "Setiawan", "Gunawan",
  "Lestari", "Kusuma", "Anggraini"
]

const streets = [
  "Melati", "Kenanga", "Anggrek", "Mawar", "Cempaka",
  "Pahlawan", "Diponegoro", "Sudirman", "Gatot Subroto"
]

function generateCustomerCode(seq: number) {
  return `DMY${String(seq).padStart(5, "0")}`
}

function generatePhone() {
  return `08${randomInt(1000000000, 9999999999)}`
}

function generateKTP() {
  return String(randomInt(1000000000000000, 9999999999999999))
}

function randomNormal(mean: number, stdDev: number) {
  let u = 0
  let v = 0

  while (u === 0) u = Math.random()
  while (v === 0) v = Math.random()

  const num = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v)

  return mean + stdDev * num
}

function randomTransactionsPerDay() {
  const val = Math.round(randomNormal(50, 12)) // mean=50, std=12
  return Math.max(20, Math.min(80, val))
}

/* ------------------ trx helpers ------------------ */

function generateTrxCode(branchCode: string, date: Date, seq: number) {
  const dd = String(date.getDate()).padStart(2, "0")
  const mm = String(date.getMonth() + 1).padStart(2, "0")
  const yy = String(date.getFullYear()).slice(-2)
  const num = String(seq).padStart(3, "0")

  return `${branchCode}${dd}${mm}${yy}${num}`
}


function randomTransactionStatus(): TransactionStatus {
  const r = Math.random()

  if (r < 0.01) return TransactionStatus.OPEN
  if (r < 0.02) return TransactionStatus.WAITING_PAYMENT
  if (r < 0.03) return TransactionStatus.PAID
  if (r < 0.93) return TransactionStatus.COMPLETED
  if (r < 0.98) return TransactionStatus.CANCELED
  return TransactionStatus.REJECTED
}
/* ------------------ seed customers ------------------ */

async function seedCustomers() {

  console.log("Generating dummy customers...")

  const total = 200

  for (let i = 1; i <= total; i++) {

    const first = pick(firstNames)
    const last = pick(lastNames)

    await prisma.customer.create({
      data: {
        code: generateCustomerCode(i),
        fullName: `${first} ${last}`,
        phoneNumber: generatePhone(),
        ktpNumber: Math.random() > 0.3 ? generateKTP() : null,
        ktpPhotoUrl: null,
        address: `Jl. ${pick(streets)} No ${randomInt(1, 200)}`,
      }
    })
  }

  console.log(`Generated ${total} dummy customers`)
}

/* ------------------ seed transactions ------------------ */

async function seedTransactions() {

  console.log("Generating dummy transactions...")

  const start = new Date("2026-01-01")
  const end = new Date("2026-03-17")

  const customers = await prisma.customer.findMany({
    where: { code: { startsWith: "DMY" } }
  })
  const itemTypes = await prisma.itemType.findMany()
  const banks = await prisma.bank.findMany()

  const frontliners = await prisma.user.findMany({
    where: {
      role: {
        code: "FRONTLINER",
      },
    },
    include: {
      branches: {
        include: {
          branch: true,
        },
      },
    },
  })

  let seqMap: Record<string, number> = {}

  for (
    let d = new Date(start);
    d <= end;
    d.setDate(d.getDate() + 1)
  ) {

    const trxCount = randomTransactionsPerDay()

    for (let i = 0; i < trxCount; i++) {

      const customer = pick(customers)
      const status = randomTransactionStatus()

      const creator = pick(frontliners)
      if (!creator.branches.length) continue

      const branch = pick(creator.branches).branch

      if (!seqMap[branch.code]) seqMap[branch.code] = 1

      const trxCode = generateTrxCode(branch.code, d, seqMap[branch.code]++)

      const itemCount = randomInt(1, 3)

      let totalNet = 0
      let totalGross = 0
      let totalAmount = 0

      const items: any[] = []

      for (let j = 0; j < itemCount; j++) {

        const gross = Number(randomBetween(1, 20).toFixed(3))
        const net = Number((gross * randomBetween(0.7, 1)).toFixed(3))

        const pricePerGram = randomBetween(700000, 2800000)
        const total = Number((net * pricePerGram).toFixed(2))

        totalGross += gross
        totalNet += net
        totalAmount += total

        items.push({
          id: randomUUID(),
          itemTypeId: pick(itemTypes).id,
          grossWeight: gross,
          netWeight: net,
          pricePerGram,
          pricePerGramAt: d,
          purityPercent: randomBetween(60, 100),
          totalPrice: total,
          caratId: "K24",
          itemCode: `ITM-${Date.now()}-${j}`,
        })
      }

      const trx = await prisma.transaction.create({
        data: {
          trxCode,
          branchId: branch.id,
          customerId: customer.id,
          createdBy: creator.id,
          createdAt: new Date(d),

          status,

          totalGrossWeight: totalGross,
          totalNetWeight: totalNet,
          totalAmount,

          bankId:
            status === TransactionStatus.PAID ||
              status === TransactionStatus.COMPLETED
              ? pick(banks).id
              : null,

          bankAccountName: customer.fullName,
          bankAccountNo: String(randomInt(1000000000, 9999999999)),

          items: { create: items },

          ...(status === TransactionStatus.CANCELED && {
            cancellationReason: "Mau cek dulu aja",
            cancelledAt: new Date(d),
            cancelledBy: creator.id,
          }),

          ...(status === TransactionStatus.COMPLETED && {
            completedAt: new Date(d),
            completedBy: creator.id,
          }),
        },
      })

      if (
        status === TransactionStatus.PAID ||
        status === TransactionStatus.COMPLETED
      ) {
        await prisma.payment.create({
          data: {
            transactionId: trx.id,
            transferBy: creator.id,
            transferDate: new Date(d),
            proofUrl: "/dummy/proof.png",
            filename: "proof.png",
            mimetype: "image/png",
            size: 1000,
          },
        })
      }
    }
  }

  console.log("Dummy transactions generated.")
}

/* ------------------ main ------------------ */

async function main() {

  console.log("Starting dummy data generation...")

  await seedCustomers()
  await seedTransactions()

  console.log("All dummy data generated.")
}

main()
  .catch(console.error)
  .finally(async () => {
    await prisma.$disconnect()
  })