import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { LoggingService } from 'src/database/logging/logging.service';
import { TeamRolesEnum } from 'src/team-roles/team-roles.enum';
import { TeamAccessService } from 'src/teams/team-access.service';
import { Repository } from 'typeorm';
import { RecurringTransactionIntervalEnum } from 'src/recurring-transactions/recurring-transaction-interval.enum';
import { CashboxExportSubscriptionResponseDTO } from './dto/cashbox-export-subscription-response.dto';
import { UpsertCashboxExportSubscriptionDTO } from './dto/upsert-cashbox-export-subscription.dto';
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 CashboxExportSubscriptionService {
  constructor(
    @InjectRepository(CashboxExportSubscription)
    private readonly repository: Repository<CashboxExportSubscription>,
    private readonly access: TeamAccessService,
    private readonly logger: LoggingService,
  ) {}

  async getSubscription(
    teamId: number,
    userId: number,
  ): Promise<CashboxExportSubscriptionResponseDTO> {
    await this.assertAccess(userId, teamId);
    const existing = await this.repository.findOne({ where: { team: { id: teamId } } });
    if (!existing) {
      return {
        recipients: [],
        interval: RecurringTransactionIntervalEnum.monthly,
        active: false,
        nextRunDate: null,
      };
    }
    return this.toResponse(existing);
  }

  async upsertSubscription(
    teamId: number,
    userId: number,
    dto: UpsertCashboxExportSubscriptionDTO,
  ): Promise<CashboxExportSubscriptionResponseDTO> {
    await this.assertAccess(userId, teamId);
    if (dto.active && dto.recipients.length === 0) {
      throw new BadRequestException(
        'Ein aktivierter automatischer Versand benötigt mindestens eine Empfängeradresse.',
      );
    }

    const existing = await this.repository.findOne({ where: { team: { id: teamId } } });
    const needsNewSchedule =
      !existing || (dto.active && (!existing.active || existing.interval !== dto.interval));

    const entity =
      existing ??
      this.repository.create({ team: { id: teamId } as any, nextRunDate: null, active: false });

    entity.recipients = dto.recipients;
    entity.interval = dto.interval;
    entity.active = dto.active;
    if (needsNewSchedule) {
      entity.nextRunDate = this.nextBoundary(dto.interval);
    }

    const saved = await this.repository.save(entity);
    await this.logger.info({
      event: 'cashbox_export_subscription_update',
      details: `teamId=${teamId} active=${dto.active} interval=${dto.interval} recipients=${dto.recipients.length}`,
      userId,
    });
    return this.toResponse(saved);
  }

  private async assertAccess(userId: number, teamId: number): Promise<void> {
    await this.access.assertAtLeast(
      userId,
      teamId,
      'transaction_create_min_role',
      TeamRolesEnum.scnd_treasurer,
    );
  }

  private nextBoundary(interval: RecurringTransactionIntervalEnum): string {
    const now = new Date();
    const date = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
    date.setUTCMonth(date.getUTCMonth() + INTERVAL_MONTHS[interval]);
    return date.toISOString();
  }

  private toResponse(
    entity: CashboxExportSubscription,
  ): CashboxExportSubscriptionResponseDTO {
    return {
      recipients: entity.recipients,
      interval: entity.interval,
      active: entity.active,
      nextRunDate: entity.nextRunDate,
    };
  }
}
