// Daily-ish generator for the partner payout ledger. Mirrors ArchiveWorker:
// a cheap hourly tick with a once-per-day guard, no cron lib needed. Each run
// (re)generates the CURRENT month's payout rows from live MRR × marginPct, so
// the partner Payouts tab reflects this month's accrual; past months freeze at
// their last snapshot once the month rolls over. The operator UI can force a
// run (any period) via POST /billing/payouts/generate regardless of this timer.

import { Injectable, Logger, type OnApplicationBootstrap, type OnModuleDestroy } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { BillingService } from './billing.service.js'

const TICK_MS = 60 * 60 * 1000 // 1 hour; a day-guard limits real work to once/day.
const DEFAULT_RUN_HOUR_UTC = 2 // 02:00 UTC daily (before the audit archive at 03:00).

@Injectable()
export class PayoutWorker implements OnApplicationBootstrap, OnModuleDestroy {
  private readonly logger = new Logger(PayoutWorker.name)
  private readonly enabled: boolean
  private readonly runHour: number
  private timer: NodeJS.Timeout | null = null
  private lastRunDay: string | null = null

  constructor(
    private readonly billing: BillingService,
    config: ConfigService,
  ) {
    // On by default — payout generation is cheap, derived, and Stripe-independent.
    // Set PAYOUTS_AUTOGEN=false to leave it to manual operator runs only.
    this.enabled = config.get('PAYOUTS_AUTOGEN') !== 'false'
    this.runHour = Number(config.get('PAYOUTS_RUN_HOUR_UTC') ?? DEFAULT_RUN_HOUR_UTC)
  }

  onApplicationBootstrap(): void {
    if (!this.enabled) {
      this.logger.log('PAYOUTS_AUTOGEN=false — payout scheduler dormant. Operator UI can force runs.')
      return
    }
    this.logger.log(`Payout scheduler active · runs daily at ${this.runHour}:00 UTC`)
    // Generate once on startup so the current month's ledger exists immediately;
    // the day-guard then prevents repeat runs within the same UTC day.
    void this.run('startup')
    this.timer = setInterval(() => void this.tick(), TICK_MS)
  }

  onModuleDestroy(): void {
    if (this.timer) clearInterval(this.timer)
  }

  private async tick(): Promise<void> {
    if (new Date().getUTCHours() !== this.runHour) return
    const today = new Date().toISOString().slice(0, 10)
    if (this.lastRunDay === today) return
    await this.run('tick')
  }

  private async run(trigger: string): Promise<void> {
    this.lastRunDay = new Date().toISOString().slice(0, 10)
    try {
      const res = await this.billing.generatePayouts()
      this.logger.log(`Payout run (${trigger}) for ${res.periodMonth}: ${res.rows} row(s)`)
    } catch (err) {
      this.logger.error(
        `Payout run (${trigger}) failed: ${err instanceof Error ? err.message : String(err)}`,
      )
    }
  }
}
