import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { PrismaService } from '../../infra/prisma.service';
import { RbacService } from '../rbac.service';
import {
  BOTFATHER_BOT_USERNAME,
  BOTFATHER_DISPLAY_NAME,
  BOTFATHER_SETTING_BOT_ID,
  BOTFATHER_SETTING_USER_ID,
  BOTFATHER_USER_USERNAME,
  GET_MY_ID_BOT_USERNAME,
  GET_MY_ID_DISPLAY_NAME,
  GET_MY_ID_SETTING_BOT_ID,
  GET_MY_ID_SETTING_USER_ID,
  GET_MY_ID_USER_USERNAME,
  SYSTEM_BOT_OWNER_EMAIL
} from './bot-father.constants';
import { generateTelegramStyleBotToken, hashBotToken } from './bot-token.util';

@Injectable()
export class BotFatherSeedService implements OnModuleInit {
  private readonly logger = new Logger(BotFatherSeedService.name);
  private cachedUserId: string | null = null;
  private cachedBotId: string | null = null;
  private cachedGetMyIdUserId: string | null = null;
  private cachedGetMyIdBotId: string | null = null;

  constructor(
    private readonly prisma: PrismaService,
    private readonly rbac: RbacService
  ) {}

  async onModuleInit() {
    await this.ensureBotFather();
    await this.ensureGetMyIdBot();
  }

  async getBotFatherUserId() {
    if (this.cachedUserId) return this.cachedUserId;
    const setting = await this.prisma.systemSetting.findUnique({ where: { key: BOTFATHER_SETTING_USER_ID } });
    if (setting?.value) {
      this.cachedUserId = setting.value;
      return setting.value;
    }
    const ensured = await this.ensureBotFather();
    return ensured.userId;
  }

  async getBotFatherBotId() {
    if (this.cachedBotId) return this.cachedBotId;
    const setting = await this.prisma.systemSetting.findUnique({ where: { key: BOTFATHER_SETTING_BOT_ID } });
    if (setting?.value) {
      this.cachedBotId = setting.value;
      return setting.value;
    }
    const ensured = await this.ensureBotFather();
    return ensured.botId;
  }

  async getGetMyIdBotUserId() {
    if (this.cachedGetMyIdUserId) return this.cachedGetMyIdUserId;
    const setting = await this.prisma.systemSetting.findUnique({ where: { key: GET_MY_ID_SETTING_USER_ID } });
    if (setting?.value) {
      this.cachedGetMyIdUserId = setting.value;
      return setting.value;
    }
    const ensured = await this.ensureGetMyIdBot();
    return ensured.userId;
  }

  async getGetMyIdBotId() {
    if (this.cachedGetMyIdBotId) return this.cachedGetMyIdBotId;
    const setting = await this.prisma.systemSetting.findUnique({ where: { key: GET_MY_ID_SETTING_BOT_ID } });
    if (setting?.value) {
      this.cachedGetMyIdBotId = setting.value;
      return setting.value;
    }
    const ensured = await this.ensureGetMyIdBot();
    return ensured.botId;
  }

  async ensureBotFather() {
    const existingBotId = await this.prisma.systemSetting.findUnique({ where: { key: BOTFATHER_SETTING_BOT_ID } });
    const existingUserId = await this.prisma.systemSetting.findUnique({ where: { key: BOTFATHER_SETTING_USER_ID } });
    if (existingBotId?.value && existingUserId?.value) {
      this.cachedBotId = existingBotId.value;
      this.cachedUserId = existingUserId.value;
      return { userId: existingUserId.value, botId: existingBotId.value };
    }

    const ownerId = await this.resolveBotOwnerId();
    let botFatherUser = await this.prisma.user.findFirst({
      where: { username: BOTFATHER_USER_USERNAME, isSystemAccount: true }
    });

    if (!botFatherUser) {
      botFatherUser = await this.prisma.user.create({
        data: {
          displayName: BOTFATHER_DISPLAY_NAME,
          username: BOTFATHER_USER_USERNAME,
          isSystemAccount: true,
          isVerified: true,
          verificationIcon: 'bot'
        }
      });
      this.logger.log(`Создан системный пользователь ${BOTFATHER_DISPLAY_NAME}`);
    }

    let bot = await this.prisma.bot.findUnique({ where: { username: BOTFATHER_BOT_USERNAME } });
    if (!bot) {
      const { token, tokenPrefix } = generateTelegramStyleBotToken();
      bot = await this.prisma.bot.create({
        data: {
          name: BOTFATHER_DISPLAY_NAME,
          username: BOTFATHER_BOT_USERNAME,
          tokenHash: hashBotToken(token),
          tokenPrefix,
          ownerId,
          isSystemBot: true
        }
      });
      this.logger.log(`Создан системный бот @${BOTFATHER_BOT_USERNAME}`);
    } else if (!bot.isSystemBot) {
      bot = await this.prisma.bot.update({
        where: { id: bot.id },
        data: { isSystemBot: true, isActive: true }
      });
    }

    if (botFatherUser.linkedBotId !== bot.id) {
      await this.prisma.user.update({
        where: { id: botFatherUser.id },
        data: { linkedBotId: bot.id }
      });
    }

    await this.upsertSetting(BOTFATHER_SETTING_USER_ID, botFatherUser.id, 'UUID системного пользователя BotFather');
    await this.upsertSetting(BOTFATHER_SETTING_BOT_ID, bot.id, 'UUID системного бота BotFather');
    await this.rbac.ensureBotUserRole(botFatherUser.id);

    this.cachedUserId = botFatherUser.id;
    this.cachedBotId = bot.id;
    return { userId: botFatherUser.id, botId: bot.id };
  }

  async ensureGetMyIdBot() {
    const existingBotId = await this.prisma.systemSetting.findUnique({ where: { key: GET_MY_ID_SETTING_BOT_ID } });
    const existingUserId = await this.prisma.systemSetting.findUnique({ where: { key: GET_MY_ID_SETTING_USER_ID } });
    if (existingBotId?.value && existingUserId?.value) {
      this.cachedGetMyIdBotId = existingBotId.value;
      this.cachedGetMyIdUserId = existingUserId.value;
      return { userId: existingUserId.value, botId: existingBotId.value };
    }

    const ownerId = await this.resolveBotOwnerId();
    let systemUser = await this.prisma.user.findFirst({
      where: { username: GET_MY_ID_USER_USERNAME, isSystemAccount: true }
    });

    if (!systemUser) {
      systemUser = await this.prisma.user.create({
        data: {
          displayName: GET_MY_ID_DISPLAY_NAME,
          username: GET_MY_ID_USER_USERNAME,
          isSystemAccount: true,
          isVerified: true,
          verificationIcon: 'bot'
        }
      });
      this.logger.log(`Создан системный пользователь ${GET_MY_ID_DISPLAY_NAME}`);
    }

    let bot = await this.prisma.bot.findUnique({ where: { username: GET_MY_ID_BOT_USERNAME } });
    if (!bot) {
      const { token, tokenPrefix } = generateTelegramStyleBotToken();
      bot = await this.prisma.bot.create({
        data: {
          name: GET_MY_ID_DISPLAY_NAME,
          username: GET_MY_ID_BOT_USERNAME,
          tokenHash: hashBotToken(token),
          tokenPrefix,
          ownerId,
          description: 'Показывает ваш user_id и chat_id для Bot API.',
          aboutText: 'Получите ID пользователя и chat_id для интеграций.',
          isSystemBot: true
        }
      });
      this.logger.log(`Создан системный бот @${GET_MY_ID_BOT_USERNAME}`);
    } else if (!bot.isSystemBot) {
      bot = await this.prisma.bot.update({
        where: { id: bot.id },
        data: { isSystemBot: true, isActive: true }
      });
    }

    if (systemUser.linkedBotId !== bot.id) {
      await this.prisma.user.update({
        where: { id: systemUser.id },
        data: { linkedBotId: bot.id }
      });
    }

    await this.upsertSetting(GET_MY_ID_SETTING_USER_ID, systemUser.id, 'UUID системного пользователя GetMyIdBot');
    await this.upsertSetting(GET_MY_ID_SETTING_BOT_ID, bot.id, 'UUID системного бота GetMyIdBot');
    await this.rbac.ensureBotUserRole(systemUser.id);

    this.cachedGetMyIdUserId = systemUser.id;
    this.cachedGetMyIdBotId = bot.id;
    return { userId: systemUser.id, botId: bot.id };
  }

  private async resolveBotOwnerId() {
    const superAdmin = await this.prisma.user.findFirst({
      where: { isSuperAdmin: true, deletedAt: null, status: 'ACTIVE', isSystemAccount: false },
      orderBy: { createdAt: 'asc' },
      select: { id: true }
    });
    if (superAdmin) return superAdmin.id;

    const systemOwner = await this.prisma.user.findUnique({ where: { email: SYSTEM_BOT_OWNER_EMAIL } });
    if (systemOwner) return systemOwner.id;

    const created = await this.prisma.user.create({
      data: {
        email: SYSTEM_BOT_OWNER_EMAIL,
        displayName: 'System Bot Owner',
        isSystemAccount: true
      }
    });
    return created.id;
  }

  private async upsertSetting(key: string, value: string, description: string) {
    await this.prisma.systemSetting.upsert({
      where: { key },
      create: { key, value, description },
      update: { value, description }
    });
  }
}
