import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { LoggingService } from 'src/database/logging/logging.service';
import { Team } from 'src/teams/entities/team.entity';
import { MailService } from 'src/mail/mail.service';
import { LessThanOrEqual, Repository } from 'typeorm';
import { RecurringTransactionIntervalEnum } from 'src/recurring-transactions/recurring-transaction-interval.enum';
import { buildPdf, buildRows } from './cashbox-export.utils';
import { CashboxExportSubscription } from './entities/cashbox-export-subscription.entity';

const INTERVAL_MONTHS: Record<RecurringTransactionIntervalEnum, number> = {
  [RecurringTransactionIntervalEnum.monthly]: 1,
  [RecurringTransactionIntervalEnum.quarterly]: 3,
  [RecurringTransactionIntervalEnum.yearly]: 12,
};

@Injectable()
export class CashboxExportScheduler {
  constructor(
    @InjectRepository(CashboxExportSubscription)
    private readonly subscriptionRepository: Repository<CashboxExportSubscription>,
    @InjectRepository(Team)
    private readonly teamRepository: Repository<Team>,
    private readonly mailService: MailService,
    private readonly logger: LoggingService,
  ) {}

  @Cron(CronExpression.EVERY_DAY_AT_4AM)
  async runDueSubscriptions(): Promise<void> {
    const today = new Date().toISOString();
    const due = await this.subscriptionRepository.find({
      where: { active: true, nextRunDate: LessThanOrEqual(today) },
      relations: ['team'],
    });

    for (const subscription of due) {
      await this.runOne(subscription);
    }
  }

  private async runOne(subscription: CashboxExportSubscription): Promise<void> {
    const team = await this.teamRepository.findOne({
      where: { id: subscription.team.id },
      relations: ['players', 'players.transactions', 'transactions'],
    });
    if (!team) return;

    const { from, to } = this.periodBounds(subscription.nextRunDate, subscription.interval);
    const rows = buildRows(team, from, to);
    const pdf = await buildPdf(team, rows, from, to);
    const filename = `kassenbuch_${team.alias}_${from}_${to}.pdf`;

    await this.mailService.cashboxExport(
      { to: subscription.recipients.join(', '), data: { teamName: team.name, from, to } },
      pdf,
      filename,
    );

    subscription.nextRunDate = this.advance(subscription.nextRunDate, subscription.interval);
    await this.subscriptionRepository.save(subscription);

    await this.logger.info({
      event: 'cashbox_export_subscription_run',
      details: `teamId=${team.id} recipients=${subscription.recipients.length} from=${from} to=${to}`,
      userId: -1,
    });
  }

  private periodBounds(
    nextRunDate: string,
    interval: RecurringTransactionIntervalEnum,
  ): { from: string; to: string } {
    const end = new Date(nextRunDate);
    end.setUTCDate(end.getUTCDate() - 1);
    const start = new Date(nextRunDate);
    start.setUTCMonth(start.getUTCMonth() - INTERVAL_MONTHS[interval]);
    return { from: start.toISOString().slice(0, 10), to: end.toISOString().slice(0, 10) };
  }

  private advance(nextRunDate: string, interval: RecurringTransactionIntervalEnum): string {
    const date = new Date(nextRunDate);
    date.setUTCMonth(date.getUTCMonth() + INTERVAL_MONTHS[interval]);
    return date.toISOString();
  }
}
