#!/usr/bin/env node

/**
 * Manual script to generate rent invoices and mark overdue payments
 * This script can be run manually or set up as a cron job
 *
 * Usage:
 * node scripts/generate-rent-invoices.js [companyId]
 *
 * If no companyId is provided, it will process all companies
 */

const { PrismaClient } = require('@prisma/client')
const nodemailer = require('nodemailer')
const prisma = new PrismaClient()

const transporter = nodemailer.createTransporter({
  host: process.env.SMTP_HOST,
  port: parseInt(process.env.SMTP_PORT || '587'),
  secure: false,
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
})

async function sendInvoiceNotification(tenantEmail, tenantName, invoiceNumber, amount, dueDate, companyName) {
  const subject = `Rent Invoice Generated - ${invoiceNumber}`
  const html = `
    <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
      <h2 style="color: #333;">Rent Invoice Notification</h2>
      <p>Dear ${tenantName},</p>
      <p>A new rent invoice has been generated for your account with ${companyName}.</p>
      <div style="background-color: #f5f5f5; padding: 20px; margin: 20px 0; border-radius: 5px;">
        <p><strong>Invoice Number:</strong> ${invoiceNumber}</p>
        <p><strong>Amount:</strong> UGX ${amount.toLocaleString()}</p>
        <p><strong>Due Date:</strong> ${dueDate.toLocaleDateString('en-US', {
          weekday: 'long',
          year: 'numeric',
          month: 'long',
          day: 'numeric'
        })}</p>
      </div>
      <p>Please ensure payment is made by the due date to avoid any late fees.</p>
      <p>You can view and pay your invoice through your tenant dashboard.</p>
      <p>Best regards,<br>${companyName} Team</p>
    </div>
  `

  const mailOptions = {
    from: `"${process.env.FROM_NAME}" <${process.env.FROM_EMAIL}>`,
    to: tenantEmail,
    subject,
    html,
  }

  return transporter.sendMail(mailOptions)
}

async function generateRentInvoices(companyId = null) {
  console.log('🚀 Starting rent invoice generation...')

  try {
    // Get companies to process
    let companies = []
    if (companyId) {
      const company = await prisma.company.findUnique({
        where: { id: companyId },
        select: { id: true, name: true, rentDueDay: true }
      })
      if (!company) {
        console.error(`❌ Company with ID ${companyId} not found`)
        return
      }
      companies = [company]
    } else {
      companies = await prisma.company.findMany({
        select: { id: true, name: true, rentDueDay: true }
      })
    }

    console.log(`📊 Processing ${companies.length} companies`)

    for (const company of companies) {
      console.log(`\n🏢 Processing company: ${company.name} (${company.id})`)

      const rentDueDay = company.rentDueDay || 1
      const now = new Date()
      const currentYear = now.getFullYear()
      const currentMonth = now.getMonth()

      // Calculate the due date for this month
      let dueDate = new Date(currentYear, currentMonth, rentDueDay)

      // If the due date for this month has passed, generate for next month
      if (now > dueDate) {
        dueDate = new Date(currentYear, currentMonth + 1, rentDueDay)
      }

      console.log(`📅 Rent due date: ${dueDate.toDateString()}`)

      // Find all active tenants for this company
      const tenants = await prisma.tenant.findMany({
        where: {
          companyId: company.id,
          status: 'active',
          leaseEndDate: {
            gte: new Date() // Lease hasn't ended yet
          }
        },
        include: {
          unit: {
            select: {
              id: true,
              unitNumber: true,
              property: {
                select: {
                  name: true
                }
              }
            }
          }
        }
      })

      console.log(`👥 Found ${tenants.length} active tenants`)

      let generatedCount = 0
      let skippedCount = 0

      for (const tenant of tenants) {
        // Check if rent invoice already exists for this month
        const existingInvoice = await prisma.rentInvoice.findFirst({
          where: {
            tenantId: tenant.id,
            dueDate: {
              gte: new Date(currentYear, currentMonth, 1), // Start of current month
              lt: new Date(currentYear, currentMonth + 1, 1) // Start of next month
            }
          }
        })

        if (existingInvoice) {
          console.log(`⏭️  Skipped ${tenant.firstName} ${tenant.lastName} - invoice already exists`)
          skippedCount++
          continue
        }

        // Generate unique invoice number
        const invoiceNumber = `INV-${company.id.slice(-6).toUpperCase()}-${tenant.id.slice(-6).toUpperCase()}-${dueDate.getFullYear()}${(dueDate.getMonth() + 1).toString().padStart(2, '0')}`

        // Calculate prorated amount if needed
        let invoiceAmount = parseFloat(tenant.monthlyRent.toString())
        let description = `Monthly rent for ${dueDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}`

        // Check if tenant moved in during this month
        const moveInDate = tenant.moveInDate || tenant.leaseStartDate
        if (moveInDate && moveInDate.getFullYear() === dueDate.getFullYear() && moveInDate.getMonth() === dueDate.getMonth()) {
          const daysInMonth = new Date(dueDate.getFullYear(), dueDate.getMonth() + 1, 0).getDate()
          const daysOccupied = daysInMonth - moveInDate.getDate() + 1
          invoiceAmount = Math.round((invoiceAmount * daysOccupied) / daysInMonth)
          description = `Prorated rent for ${dueDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })} (${daysOccupied}/${daysInMonth} days)`
        }

        // Check if lease ends during this month
        if (tenant.leaseEndDate && tenant.leaseEndDate.getFullYear() === dueDate.getFullYear() && tenant.leaseEndDate.getMonth() === dueDate.getMonth()) {
          const daysInMonth = new Date(dueDate.getFullYear(), dueDate.getMonth() + 1, 0).getDate()
          const daysOccupied = tenant.leaseEndDate.getDate()
          invoiceAmount = Math.round((invoiceAmount * daysOccupied) / daysInMonth)
          description = `Prorated rent for ${dueDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })} (${daysOccupied}/${daysInMonth} days)`
        }

        // Create rent invoice
        await prisma.rentInvoice.create({
          data: {
            tenantId: tenant.id,
            unitId: tenant.unitId,
            propertyId: tenant.unit?.propertyId,
            companyId: company.id,
            invoiceNumber: invoiceNumber,
            invoiceDate: new Date(),
            dueDate: dueDate,
            amount: invoiceAmount,
            description: description,
            status: 'pending'
          }
        })

        console.log(`✅ Generated invoice for ${tenant.firstName} ${tenant.lastName} - Unit ${tenant.unit.unitNumber} - UGX ${invoiceAmount.toLocaleString()}`)

        // Send email notification
        if (tenant.email) {
          try {
            await sendInvoiceNotification(
              tenant.email,
              `${tenant.firstName} ${tenant.lastName}`,
              invoiceNumber,
              invoiceAmount,
              dueDate,
              company.name
            )
            console.log(`📧 Email sent to ${tenant.email}`)
          } catch (emailError) {
            console.error(`❌ Failed to send email to ${tenant.email}:`, emailError.message)
          }
        }

        generatedCount++
      }

      console.log(`📈 Company ${company.name}: Generated ${generatedCount} invoices, skipped ${skippedCount}`)
    }

    console.log('\n🎉 Rent invoice generation completed!')

  } catch (error) {
    console.error('❌ Error generating rent invoices:', error)
  } finally {
    await prisma.$disconnect()
  }
}

async function markOverduePayments(companyId = null) {
  console.log('🔍 Starting overdue payment marking...')

  try {
    const now = new Date()

    // Find all pending payments that are past their due date
    const whereClause = {
      status: 'pending',
      dueDate: {
        lt: now
      }
    }

    if (companyId) {
      whereClause.companyId = companyId
    }

    const overduePayments = await prisma.payment.findMany({
      where: whereClause,
      include: {
        tenant: {
          select: {
            firstName: true,
            lastName: true,
            unit: {
              select: {
                unitNumber: true,
                property: {
                  select: {
                    name: true
                  }
                }
              }
            }
          }
        }
      }
    })

    if (overduePayments.length === 0) {
      console.log('✅ No overdue payments found')
      return
    }

    console.log(`📋 Found ${overduePayments.length} overdue payments`)

    // Update payments to overdue status
    const updatePromises = overduePayments.map(payment =>
      prisma.payment.update({
        where: { id: payment.id },
        data: { status: 'overdue' }
      })
    )

    await Promise.all(updatePromises)

    console.log('📊 Overdue payments marked:')
    overduePayments.forEach(payment => {
      const daysOverdue = Math.floor((now.getTime() - payment.dueDate.getTime()) / (1000 * 60 * 60 * 24))
      console.log(`  - ${payment.tenant.firstName} ${payment.tenant.lastName} (Unit ${payment.tenant.unit.unitNumber}): UGX ${payment.amount.toLocaleString()} - ${daysOverdue} days overdue`)
    })

    console.log('\n✅ Overdue payment marking completed!')

  } catch (error) {
    console.error('❌ Error marking overdue payments:', error)
  } finally {
    await prisma.$disconnect()
  }
}

// Main execution
async function main() {
  const args = process.argv.slice(2)
  const command = args[0] || 'all'
  const companyId = args[1]

  switch (command) {
    case 'generate':
      await generateRentInvoices(companyId)
      break
    case 'overdue':
      await markOverduePayments(companyId)
      break
    case 'all':
    default:
      await generateRentInvoices(companyId)
      console.log('\n' + '='.repeat(50) + '\n')
      await markOverduePayments(companyId)
      break
  }
}

// Run the script
main().catch(console.error)