import { createModuleLogger } from '../../utils/index.js';
import { TelegramBotClient } from './bot-client.js';
import { WebhookHandler } from './webhook-handler.js';
import { ChannelManager } from './channel-manager.js';
import { StarsManager } from './stars-manager.js';
import type { DataStore } from '../data-store/index.js';
import type { EncryptedKeyStore } from '../data-store/encryption.js';
import type { TelegramBotInfo } from '../../types/telegram.js';

const log = createModuleLogger('telegram-manager');

const BOT_TOKEN_PREFIX = 'bot-token:';

interface BotEntry {
  characterId: string;
  client: TelegramBotClient;
  botInfo?: TelegramBotInfo;
}

export class TelegramManager {
  private readonly dataStore: DataStore;
  private readonly encryptedStore: EncryptedKeyStore;
  private readonly bots: Map<string, BotEntry> = new Map();
  private readonly tokenToCharacterId: Map<string, string> = new Map();
  private readonly webhookHandler: WebhookHandler;
  private readonly channelManager: ChannelManager;
  private readonly starsManager: StarsManager;

  constructor(dataStore: DataStore, encryptedStore: EncryptedKeyStore) {
    this.dataStore = dataStore;
    this.encryptedStore = encryptedStore;
    this.webhookHandler = new WebhookHandler(new Set<string>());
    this.channelManager = new ChannelManager(dataStore, (id) => this.getBotClient(id) as any);
    this.starsManager = new StarsManager(dataStore);
  }

  // --- Bot management ---

  async addBot(characterId: string, token: string): Promise<TelegramBotInfo> {
    log.info({ characterId }, 'Adding bot');

    const client = new TelegramBotClient(token);
    const botInfo = await client.getMe();

    await this.encryptedStore.setKey(`${BOT_TOKEN_PREFIX}${characterId}`, token);

    this.webhookHandler.registerToken(token);
    this.bots.set(characterId, { characterId, client, botInfo });
    this.tokenToCharacterId.set(token, characterId);

    log.info({ characterId, botUsername: botInfo.username }, 'Bot added successfully');
    return botInfo;
  }

  async removeBot(characterId: string): Promise<void> {
    log.info({ characterId }, 'Removing bot');

    const entry = this.bots.get(characterId);
    if (entry) {
      const token = await this.encryptedStore.getKey(`${BOT_TOKEN_PREFIX}${characterId}`);
      if (token) {
        try {
          await entry.client.deleteWebhook();
        } catch (err) {
          log.warn({ characterId, err }, 'Failed to delete webhook during bot removal');
        }
        this.webhookHandler.unregisterToken(token);
        this.tokenToCharacterId.delete(token);
      }
      this.bots.delete(characterId);
    }

    await this.encryptedStore.deleteKey(`${BOT_TOKEN_PREFIX}${characterId}`);
    this.channelManager.stopSchedule(characterId);

    log.info({ characterId }, 'Bot removed');
  }

  async updateBot(characterId: string, newToken: string): Promise<TelegramBotInfo> {
    log.info({ characterId }, 'Updating bot token');
    await this.removeBot(characterId);
    return this.addBot(characterId, newToken);
  }

  async validateBot(characterId: string): Promise<TelegramBotInfo> {
    const entry = this.bots.get(characterId);
    if (!entry) {
      throw new Error(`No bot found for character ${characterId}`);
    }
    const botInfo = await entry.client.getMe();
    entry.botInfo = botInfo;
    return botInfo;
  }

  getBotClient(characterId: string): TelegramBotClient | undefined {
    return this.bots.get(characterId)?.client;
  }

  getCharacterIdByToken(botToken: string): string | undefined {
    return this.tokenToCharacterId.get(botToken);
  }

  // --- Webhook management ---

  async setupWebhook(characterId: string, baseUrl: string): Promise<void> {
    const entry = this.bots.get(characterId);
    if (!entry) {
      throw new Error(`No bot found for character ${characterId}`);
    }

    const token = await this.encryptedStore.getKey(`${BOT_TOKEN_PREFIX}${characterId}`);
    if (!token) {
      throw new Error(`No token found for character ${characterId}`);
    }

    const webhookUrl = `${baseUrl}/api/v1/webhooks/telegram/${token}`;
    await entry.client.setWebhook(webhookUrl);
    log.info({ characterId }, 'Webhook set up');
  }

  async removeWebhook(characterId: string): Promise<void> {
    const entry = this.bots.get(characterId);
    if (!entry) {
      throw new Error(`No bot found for character ${characterId}`);
    }
    await entry.client.deleteWebhook();
    log.info({ characterId }, 'Webhook removed');
  }

  getWebhookHandler(): WebhookHandler {
    return this.webhookHandler;
  }

  // --- Channel management ---

  getChannelManager(): ChannelManager {
    return this.channelManager;
  }

  // --- Stars management ---

  getStarsManager(): StarsManager {
    return this.starsManager;
  }

  // --- Lifecycle ---

  async start(): Promise<void> {
    log.info('Starting TelegramManager');

    const keys = await this.encryptedStore.listKeys();
    const botKeys = keys.filter((k) => k.name.startsWith(BOT_TOKEN_PREFIX));

    for (const key of botKeys) {
      const characterId = key.name.slice(BOT_TOKEN_PREFIX.length);
      try {
        const token = await this.encryptedStore.getKey(key.name);
        if (!token) continue;

        const client = new TelegramBotClient(token);
        let botInfo: TelegramBotInfo | undefined;
        try {
          botInfo = await client.getMe();
        } catch (err) {
          log.warn({ characterId, err }, 'Failed to validate bot on startup');
        }

        this.webhookHandler.registerToken(token);
        this.bots.set(characterId, { characterId, client, botInfo });
        this.tokenToCharacterId.set(token, characterId);
        log.info({ characterId, botUsername: botInfo?.username }, 'Bot loaded');
      } catch (err) {
        log.error({ characterId, err }, 'Failed to load bot on startup');
      }
    }

    log.info({ botCount: this.bots.size }, 'TelegramManager started');
  }

  async stop(): Promise<void> {
    log.info('Stopping TelegramManager');
    this.channelManager.stopAllSchedules();

    for (const [characterId, entry] of this.bots) {
      try {
        await entry.client.deleteWebhook();
      } catch (err) {
        log.warn({ characterId, err }, 'Failed to delete webhook during shutdown');
      }
    }

    this.bots.clear();
    this.tokenToCharacterId.clear();
    log.info('TelegramManager stopped');
  }

  // --- Status ---

  getActiveBots(): Map<string, { characterId: string; botInfo?: TelegramBotInfo }> {
    const result = new Map<string, { characterId: string; botInfo?: TelegramBotInfo }>();
    for (const [id, entry] of this.bots) {
      result.set(id, { characterId: entry.characterId, botInfo: entry.botInfo });
    }
    return result;
  }

  async getBotStatus(characterId: string): Promise<{ active: boolean; botInfo?: TelegramBotInfo }> {
    const entry = this.bots.get(characterId);
    if (!entry) {
      return { active: false };
    }
    return { active: true, botInfo: entry.botInfo };
  }
}
