import { Injectable, Logger } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../../../prisma/prisma.service';
import { TelegramApiService } from './telegram-api.service';
import { BotScenario, ReferralNode } from '../interfaces/scenario.interface';

/**
 * ReferralService handles the complete referral lifecycle:
 *
 * 1. Friend clicks ref link → /start ref_{subscriberId}
 * 2. Friend gets routed to friendStartNode
 * 3. Referrer's invitedCount is incremented
 * 4. Referrer gets a notification ("Friend X joined! Y more to go")
 * 5. When targetCount reached → referrer advances to onReach node
 */
@Injectable()
export class ReferralService {
  private readonly logger = new Logger(ReferralService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly telegramApi: TelegramApiService,
  ) {}

  /**
   * Called when a new subscriber joins via ref link.
   * Notifies the referrer and checks if target is reached.
   *
   * Returns the friendStartNode ID if the referrer has a referral node in scenario.
   */
  async onFriendJoined(
    botId: string,
    botToken: string,
    referrerId: string,
    friendName: string,
    scenario: BotScenario | null,
  ): Promise<string | null> {
    // Get referrer with updated count
    const referrer = await this.prisma.tgBotSubscriber.findUnique({
      where: { id: referrerId },
    });
    if (!referrer) return null;

    // Find the referral node in scenario to get config
    const referralNode = scenario ? this.findReferralNode(scenario) : null;

    const targetCount = referralNode?.data.targetCount ?? 2;
    const currentCount = referrer.invitedCount;
    const remaining = Math.max(0, targetCount - currentCount);

    // Notify referrer
    let notifyText: string;
    if (remaining <= 0) {
      notifyText = `🎉 <b>${friendName}</b> принял(а) приглашение!\n\nВы выполнили условие — бонус начислен!`;
    } else if (remaining === 1) {
      notifyText = `👤 <b>${friendName}</b> принял(а) приглашение!\n\nОсталось пригласить ещё <b>1 человека</b>, и вы получите бонус!`;
    } else {
      notifyText = `👤 <b>${friendName}</b> принял(а) приглашение!\n\nОсталось ещё <b>${remaining}</b> чел. для получения бонуса.`;
    }

    await this.telegramApi
      .sendMessage(botToken, {
        chat_id: referrer.chatId,
        text: notifyText,
        parse_mode: 'HTML',
      })
      .catch((err: unknown) => {
        this.logger.warn(
          `Failed to notify referrer ${referrerId}: ${err instanceof Error ? err.message : String(err)}`,
        );
      });

    // Log notification
    await this.prisma.tgBotMessage.create({
      data: {
        botId,
        subscriberId: referrerId,
        direction: 'OUT',
        text: notifyText,
        nodeId: 'system_referral_notify',
        payload: Prisma.JsonNull,
      },
    });

    // If target reached — advance referrer to onReach node
    if (remaining <= 0 && referralNode?.data.onReach) {
      await this.prisma.tgBotSubscriber.update({
        where: { id: referrerId },
        data: {
          currentNodeId: referralNode.data.onReach,
          variables: this.mergeVariable(
            (referrer.variables as Record<string, unknown>) ?? {},
            referralNode.data.trackVariable,
            currentCount,
          ),
        },
      });

      // Trigger engine for referrer to execute the onReach node
      // We return a flag so the caller can handle it
      this.logger.log(
        `Referrer ${referrerId} reached target ${targetCount}, advancing to ${referralNode.data.onReach}`,
      );
    } else {
      // Update referrer variables with current count
      await this.prisma.tgBotSubscriber.update({
        where: { id: referrerId },
        data: {
          variables: this.mergeVariable(
            (referrer.variables as Record<string, unknown>) ?? {},
            referralNode?.data.trackVariable ?? 'invited_count',
            currentCount,
          ),
        },
      });
    }

    // Return friendStartNode for routing the friend
    return referralNode?.data.friendStartNode ?? null;
  }

  /**
   * Generate referral link for a subscriber.
   */
  generateLink(botUsername: string, subscriberId: string): string {
    return `https://t.me/${botUsername}?start=ref_${subscriberId}`;
  }

  /**
   * Get referral stats for a subscriber.
   */
  async getReferralStats(subscriberId: string) {
    const subscriber = await this.prisma.tgBotSubscriber.findUnique({
      where: { id: subscriberId },
      select: { invitedCount: true, referrerId: true },
    });

    const referrals = await this.prisma.tgBotSubscriber.findMany({
      where: { referrerId: subscriberId },
      select: {
        id: true,
        firstName: true,
        lastName: true,
        username: true,
        createdAt: true,
      },
      orderBy: { createdAt: 'desc' },
    });

    return {
      invitedCount: subscriber?.invitedCount ?? 0,
      referrals,
    };
  }

  // ─── Helpers ───────────────────────────────────────────────────

  private findReferralNode(scenario: BotScenario): ReferralNode | null {
    for (const node of Object.values(scenario.nodes)) {
      if (node.type === 'referral') return node;
    }
    return null;
  }

  private mergeVariable(
    vars: Record<string, unknown>,
    key: string,
    value: unknown,
  ): Prisma.InputJsonValue {
    return { ...vars, [key]: value } as unknown as Prisma.InputJsonValue;
  }
}
