/**
 * Billing Alert Service
 * Manages billing alerts and notifications
 *
 * @module BillingUsage
 */

import { Repository, FindOptionsWhere, In } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import {
  BillingAlert,
  BillingAlertType,
  AlertSeverity,
  AlertStatus,
} from '../entities/billing-alert.entity';

export interface ServiceContext {
  tenantId: string;
  userId?: string;
}

export interface CreateBillingAlertDto {
  alertType: BillingAlertType;
  title: string;
  message?: string;
  severity?: AlertSeverity;
  metadata?: Record<string, any>;
}

export interface UpdateBillingAlertDto {
  title?: string;
  message?: string;
  severity?: AlertSeverity;
  status?: AlertStatus;
  metadata?: Record<string, any>;
}

export interface BillingAlertFilters {
  alertType?: BillingAlertType;
  severity?: AlertSeverity;
  status?: AlertStatus;
  types?: BillingAlertType[];
}

export class BillingAlertService {
  private repository: Repository<BillingAlert>;

  constructor() {
    this.repository = AppDataSource.getRepository(BillingAlert);
  }

  async findAll(
    ctx: ServiceContext,
    filters: BillingAlertFilters = {}
  ): Promise<BillingAlert[]> {
    const where: FindOptionsWhere<BillingAlert> = {
      tenantId: ctx.tenantId,
    };

    if (filters.alertType) {
      where.alertType = filters.alertType;
    }

    if (filters.severity) {
      where.severity = filters.severity;
    }

    if (filters.status) {
      where.status = filters.status;
    }

    if (filters.types && filters.types.length > 0) {
      where.alertType = In(filters.types);
    }

    return this.repository.find({
      where,
      order: { createdAt: 'DESC' },
    });
  }

  async findById(ctx: ServiceContext, id: string): Promise<BillingAlert | null> {
    return this.repository.findOne({
      where: { id, tenantId: ctx.tenantId },
    });
  }

  async getActiveAlerts(ctx: ServiceContext): Promise<BillingAlert[]> {
    return this.repository.find({
      where: {
        tenantId: ctx.tenantId,
        status: 'active',
      },
      order: { severity: 'DESC', createdAt: 'DESC' },
    });
  }

  async getUnacknowledgedAlerts(ctx: ServiceContext): Promise<BillingAlert[]> {
    return this.repository.find({
      where: {
        tenantId: ctx.tenantId,
        status: 'active',
        acknowledgedAt: undefined,
      },
      order: { severity: 'DESC', createdAt: 'DESC' },
    });
  }

  async create(ctx: ServiceContext, data: CreateBillingAlertDto): Promise<BillingAlert> {
    const alert = this.repository.create({
      ...data,
      tenantId: ctx.tenantId,
      status: 'active',
    });
    return this.repository.save(alert);
  }

  async update(
    ctx: ServiceContext,
    id: string,
    data: UpdateBillingAlertDto
  ): Promise<BillingAlert | null> {
    const alert = await this.findById(ctx, id);
    if (!alert) {
      return null;
    }

    Object.assign(alert, data);
    return this.repository.save(alert);
  }

  async acknowledge(
    ctx: ServiceContext,
    id: string
  ): Promise<BillingAlert | null> {
    const alert = await this.findById(ctx, id);
    if (!alert) {
      return null;
    }

    alert.status = 'acknowledged';
    alert.acknowledgedAt = new Date();
    alert.acknowledgedBy = ctx.userId || null as any;

    return this.repository.save(alert);
  }

  async resolve(ctx: ServiceContext, id: string): Promise<BillingAlert | null> {
    const alert = await this.findById(ctx, id);
    if (!alert) {
      return null;
    }

    alert.status = 'resolved';
    return this.repository.save(alert);
  }

  async markAsNotified(
    ctx: ServiceContext,
    id: string
  ): Promise<BillingAlert | null> {
    const alert = await this.findById(ctx, id);
    if (!alert) {
      return null;
    }

    alert.notifiedAt = new Date();
    return this.repository.save(alert);
  }

  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const result = await this.repository.delete({
      id,
      tenantId: ctx.tenantId,
    });
    return result.affected ? result.affected > 0 : false;
  }

  // Alert creation helpers
  async createUsageLimitAlert(
    ctx: ServiceContext,
    limitType: string,
    currentUsage: number,
    limit: number,
    percentUsed: number
  ): Promise<BillingAlert> {
    const severity: AlertSeverity =
      percentUsed >= 100 ? 'critical' : percentUsed >= 90 ? 'warning' : 'info';

    return this.create(ctx, {
      alertType: 'usage_limit',
      title: `${limitType} usage at ${percentUsed.toFixed(0)}%`,
      message: `You have used ${currentUsage} of ${limit} ${limitType}. ${
        percentUsed >= 100
          ? 'You have exceeded your limit.'
          : percentUsed >= 90
          ? 'Consider upgrading your plan.'
          : 'Monitor your usage.'
      }`,
      severity,
      metadata: {
        limitType,
        currentUsage,
        limit,
        percentUsed,
      },
    });
  }

  async createPaymentDueAlert(
    ctx: ServiceContext,
    dueDate: Date,
    amount: number
  ): Promise<BillingAlert> {
    const daysUntilDue = Math.ceil(
      (dueDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
    );
    const severity: AlertSeverity =
      daysUntilDue <= 0 ? 'critical' : daysUntilDue <= 3 ? 'warning' : 'info';

    return this.create(ctx, {
      alertType: 'payment_due',
      title: `Payment of $${amount.toFixed(2)} due ${
        daysUntilDue <= 0 ? 'now' : `in ${daysUntilDue} days`
      }`,
      message: `Your subscription payment of $${amount.toFixed(
        2
      )} is due on ${dueDate.toLocaleDateString()}.`,
      severity,
      metadata: {
        dueDate: dueDate.toISOString(),
        amount,
        daysUntilDue,
      },
    });
  }

  async createPaymentFailedAlert(
    ctx: ServiceContext,
    amount: number,
    errorMessage: string
  ): Promise<BillingAlert> {
    return this.create(ctx, {
      alertType: 'payment_failed',
      title: `Payment of $${amount.toFixed(2)} failed`,
      message: `We were unable to process your payment. Error: ${errorMessage}. Please update your payment method.`,
      severity: 'critical',
      metadata: {
        amount,
        errorMessage,
        failedAt: new Date().toISOString(),
      },
    });
  }

  async createTrialEndingAlert(
    ctx: ServiceContext,
    trialEndDate: Date
  ): Promise<BillingAlert> {
    const daysRemaining = Math.ceil(
      (trialEndDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
    );

    return this.create(ctx, {
      alertType: 'trial_ending',
      title: `Your trial ends in ${daysRemaining} days`,
      message: `Your free trial will end on ${trialEndDate.toLocaleDateString()}. Upgrade now to continue using all features.`,
      severity: daysRemaining <= 3 ? 'warning' : 'info',
      metadata: {
        trialEndDate: trialEndDate.toISOString(),
        daysRemaining,
      },
    });
  }

  async createSubscriptionEndingAlert(
    ctx: ServiceContext,
    endDate: Date,
    planName: string
  ): Promise<BillingAlert> {
    const daysRemaining = Math.ceil(
      (endDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
    );

    return this.create(ctx, {
      alertType: 'subscription_ending',
      title: `Your ${planName} subscription ends in ${daysRemaining} days`,
      message: `Your subscription will end on ${endDate.toLocaleDateString()}. Renew to continue using the service.`,
      severity: daysRemaining <= 7 ? 'warning' : 'info',
      metadata: {
        endDate: endDate.toISOString(),
        planName,
        daysRemaining,
      },
    });
  }

  async getAlertCounts(ctx: ServiceContext): Promise<{
    total: number;
    byStatus: Record<AlertStatus, number>;
    bySeverity: Record<AlertSeverity, number>;
    byType: Record<BillingAlertType, number>;
  }> {
    const alerts = await this.repository.find({
      where: { tenantId: ctx.tenantId },
    });

    const byStatus: Record<AlertStatus, number> = {
      active: 0,
      acknowledged: 0,
      resolved: 0,
    };

    const bySeverity: Record<AlertSeverity, number> = {
      info: 0,
      warning: 0,
      critical: 0,
    };

    const byType: Record<BillingAlertType, number> = {
      usage_limit: 0,
      payment_due: 0,
      payment_failed: 0,
      trial_ending: 0,
      subscription_ending: 0,
    };

    for (const alert of alerts) {
      byStatus[alert.status]++;
      bySeverity[alert.severity]++;
      byType[alert.alertType]++;
    }

    return {
      total: alerts.length,
      byStatus,
      bySeverity,
      byType,
    };
  }

  async resolveAlertsByType(
    ctx: ServiceContext,
    alertType: BillingAlertType
  ): Promise<number> {
    const result = await this.repository.update(
      {
        tenantId: ctx.tenantId,
        alertType,
        status: In(['active', 'acknowledged']),
      },
      {
        status: 'resolved',
      }
    );
    return result.affected || 0;
  }
}
