import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { UsersService } from "@bff/modules/users/application/users.service.js";
import { OrderOrchestrator } from "@bff/modules/orders/services/order-orchestrator.service.js";
import { InternetEligibilityService } from "@bff/modules/services/application/internet-eligibility.service.js";
import { ResidenceCardService } from "@bff/modules/verification/residence-card.service.js";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service.js";
import { WhmcsPaymentService } from "@bff/integrations/whmcs/services/whmcs-payment.service.js";
import { NotificationService } from "@bff/modules/notifications/notifications.service.js";
import {
  meStatusSchema,
  type DashboardSummary,
  type DashboardTask,
  type MeStatus,
  type PaymentMethodsStatus,
} from "@customer-portal/domain/dashboard";
import { NOTIFICATION_SOURCE, NOTIFICATION_TYPE } from "@customer-portal/domain/notifications";
import type { InternetEligibilityDetails } from "@customer-portal/domain/services";
import {
  residenceCardVerificationSchema,
  type ResidenceCardVerification,
} from "@customer-portal/domain/customer";
import type { OrderSummary } from "@customer-portal/domain/orders";

/**
 * Me Status Aggregator
 *
 * Read-only aggregator that combines data from multiple sources
 * (users, orders, eligibility, verification, payments, notifications)
 * to build the dashboard status response.
 */
@Injectable()
export class MeStatusAggregator {
  // eslint-disable-next-line max-params -- NestJS DI requires injecting multiple services for aggregation
  constructor(
    private readonly users: UsersService,
    private readonly orders: OrderOrchestrator,
    private readonly internetEligibility: InternetEligibilityService,
    private readonly residenceCards: ResidenceCardService,
    private readonly mappings: MappingsService,
    private readonly whmcsPayments: WhmcsPaymentService,
    private readonly notifications: NotificationService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  async getStatusForUser(userId: string): Promise<MeStatus> {
    try {
      const [summary, internetEligibility, residenceCardVerification, orders] = await Promise.all([
        this.users.getUserSummary(userId),
        this.internetEligibility.getEligibilityDetailsForUser(userId),
        this.safeGetResidenceCardVerification(userId),
        this.safeGetOrders(userId),
      ]);

      const paymentMethods = await this.safeGetPaymentMethodsStatus(userId);

      const tasks = this.computeTasks({
        summary,
        paymentMethods,
        internetEligibility,
        residenceCardVerification,
        orders,
      });

      await this.maybeCreateInvoiceDueNotification(userId, summary);

      return meStatusSchema.parse({
        summary,
        paymentMethods,
        internetEligibility,
        residenceCardVerification,
        tasks,
      });
    } catch (error) {
      this.logger.error({ userId, err: error }, "Failed to get status for user");
      throw error;
    }
  }

  private async safeGetResidenceCardVerification(
    userId: string
  ): Promise<ResidenceCardVerification> {
    try {
      return await this.residenceCards.getStatusForUser(userId);
    } catch (error) {
      this.logger.warn(
        { userId, err: error instanceof Error ? error.message : String(error) },
        "Failed to load residence card verification for status payload"
      );
      return residenceCardVerificationSchema.parse({
        status: "not_submitted",
        filename: null,
        mimeType: null,
        sizeBytes: null,
        submittedAt: null,
        reviewedAt: null,
        reviewerNotes: null,
      });
    }
  }

  private async safeGetOrders(userId: string): Promise<OrderSummary[] | null> {
    try {
      const result = await this.orders.getOrdersForUser(userId);
      return Array.isArray(result) ? result : [];
    } catch (error) {
      this.logger.warn(
        { userId, err: error instanceof Error ? error.message : String(error) },
        "Failed to load orders for status payload"
      );
      return null;
    }
  }

  private async safeGetPaymentMethodsStatus(userId: string): Promise<PaymentMethodsStatus> {
    try {
      const mapping = await this.mappings.findByUserId(userId);
      if (!mapping?.whmcsClientId) {
        return { totalCount: null };
      }

      const list = await this.whmcsPayments.getPaymentMethods(mapping.whmcsClientId, userId);
      return { totalCount: typeof list?.totalCount === "number" ? list.totalCount : 0 };
    } catch (error) {
      this.logger.warn(
        { userId, err: error instanceof Error ? error.message : String(error) },
        "Failed to load payment methods for status payload"
      );
      return { totalCount: null };
    }
  }

  private computeTasks(params: {
    summary: DashboardSummary;
    paymentMethods: PaymentMethodsStatus;
    internetEligibility: InternetEligibilityDetails;
    residenceCardVerification: ResidenceCardVerification;
    orders: OrderSummary[] | null;
  }): DashboardTask[] {
    const tasks: DashboardTask[] = [];

    const { summary, paymentMethods, internetEligibility, residenceCardVerification, orders } =
      params;

    this.addInvoiceTask(tasks, summary);
    this.addPaymentMethodTask(tasks, paymentMethods);
    this.addPendingOrderTask(tasks, orders);
    this.addInternetEligibilityTask(tasks, internetEligibility);
    this.addIdVerificationTask(tasks, residenceCardVerification);
    this.addOnboardingTask(tasks, summary);

    return tasks.sort((a, b) => a.priority - b.priority);
  }

  private addInvoiceTask(tasks: DashboardTask[], summary: DashboardSummary): void {
    if (!summary.nextInvoice) {
      return;
    }

    const dueDate = new Date(summary.nextInvoice.dueDate);
    const isValid = !Number.isNaN(dueDate.getTime());
    const isOverdue = isValid ? dueDate.getTime() < Date.now() : false;

    const formattedAmount = new Intl.NumberFormat("en-US", {
      style: "currency",
      currency: summary.nextInvoice.currency,
      maximumFractionDigits: 0,
    }).format(summary.nextInvoice.amount);

    const dueText = isValid
      ? dueDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })
      : "soon";

    tasks.push({
      id: `invoice-${summary.nextInvoice.id}`,
      priority: 1,
      type: "invoice",
      title: isOverdue ? "Pay overdue invoice" : "Pay upcoming invoice",
      description: `Invoice #${summary.nextInvoice.id} · ${formattedAmount} · Due ${dueText}`,
      actionLabel: "Pay now",
      detailHref: `/account/billing/invoices/${summary.nextInvoice.id}`,
      requiresSsoAction: true,
      tone: "critical",
      metadata: {
        invoiceId: summary.nextInvoice.id,
        amount: summary.nextInvoice.amount,
        currency: summary.nextInvoice.currency,
        ...(isValid ? { dueDate: dueDate.toISOString() } : {}),
      },
    });
  }

  private addPaymentMethodTask(tasks: DashboardTask[], paymentMethods: PaymentMethodsStatus): void {
    if (paymentMethods.totalCount !== 0) {
      return;
    }

    tasks.push({
      id: "add-payment-method",
      priority: 2,
      type: "payment_method",
      title: "Add a payment method",
      description: "Required to place orders and process invoices",
      actionLabel: "Add method",
      detailHref: "/account/billing/payments",
      requiresSsoAction: true,
      tone: "warning",
    });
  }

  private addPendingOrderTask(tasks: DashboardTask[], orders: OrderSummary[] | null): void {
    if (!orders || orders.length === 0) {
      return;
    }

    const pendingOrder = orders.find(
      o =>
        o.status === "Draft" ||
        o.status === "Pending" ||
        (o.status === "Processed" && o.activationStatus !== "Completed")
    );

    if (!pendingOrder) {
      return;
    }

    const statusText = this.getOrderStatusText(pendingOrder.status);

    tasks.push({
      id: `order-${pendingOrder.id}`,
      priority: 3,
      type: "order",
      title: "Order in progress",
      description: `${pendingOrder.orderType || "Your"} order is ${statusText}`,
      actionLabel: "View details",
      detailHref: `/account/orders/${pendingOrder.id}`,
      tone: "info",
      metadata: { orderId: pendingOrder.id },
    });
  }

  private getOrderStatusText(status: string): string {
    switch (status) {
      case "Pending":
        return "awaiting review";
      case "Draft":
        return "in draft";
      default:
        return "being activated";
    }
  }

  private addInternetEligibilityTask(
    tasks: DashboardTask[],
    internetEligibility: InternetEligibilityDetails
  ): void {
    if (internetEligibility.status !== "pending") {
      return;
    }

    tasks.push({
      id: "internet-eligibility-review",
      priority: 4,
      type: "internet_eligibility",
      title: "Internet availability review",
      description:
        "We're verifying if our service is available at your residence. We'll notify you when review is complete.",
      actionLabel: "View status",
      detailHref: "/account/services/internet",
      tone: "info",
    });
  }

  private addIdVerificationTask(
    tasks: DashboardTask[],
    residenceCardVerification: ResidenceCardVerification
  ): void {
    if (residenceCardVerification.status !== "rejected") {
      return;
    }

    tasks.push({
      id: "id-verification-rejected",
      priority: 4,
      type: "id_verification",
      title: "ID verification requires attention",
      description: "We couldn't verify your ID. Please review the feedback and resubmit.",
      actionLabel: "Resubmit",
      detailHref: "/account/settings/verification",
      tone: "warning",
    });
  }

  private addOnboardingTask(tasks: DashboardTask[], summary: DashboardSummary): void {
    if (summary.stats.activeSubscriptions !== 0 || tasks.length !== 0) {
      return;
    }

    tasks.push({
      id: "start-subscription",
      priority: 4,
      type: "onboarding",
      title: "Start your first service",
      description: "Browse our catalog and subscribe to internet, SIM, or VPN",
      actionLabel: "Browse services",
      detailHref: "/services",
      tone: "neutral",
    });
  }

  private async maybeCreateInvoiceDueNotification(
    userId: string,
    summary: DashboardSummary
  ): Promise<void> {
    const invoice = summary.nextInvoice;
    if (!invoice) return;

    try {
      const dueDate = new Date(invoice.dueDate);
      if (Number.isNaN(dueDate.getTime())) return;

      const daysUntilDue = (dueDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24);
      // Notify when due within a week (or overdue).
      if (daysUntilDue > 7) return;

      await this.notifications.createNotification({
        userId,
        type: NOTIFICATION_TYPE.INVOICE_DUE,
        source: NOTIFICATION_SOURCE.SYSTEM,
        sourceId: `invoice:${invoice.id}`,
        actionUrl: `/account/billing/invoices/${invoice.id}`,
      });
    } catch (error) {
      this.logger.warn(
        { userId, err: error instanceof Error ? error.message : String(error) },
        "Failed to create invoice due notification"
      );
    }
  }
}
