import { EventEmitter } from 'node:events'
import fs from 'node:fs/promises'
import path from 'node:path'
import type { RemoteControlConfig, ConfigChangeEvent } from './types'

const DEFAULT_CONFIG: RemoteControlConfig = {
  discord: {
    enabled: false,
    botToken: '',
  },
  dingtalk: {
    enabled: false,
    clientId: '',
    clientSecret: '',
  },
  lark: {
    enabled: false,
    appId: '',
    appSecret: '',
  },
  telegram: {
    enabled: false,
    botToken: '',
  },
  slack: {
    enabled: false,
    botToken: '',
    appToken: '',
  },
}

function maskValue(value: string): string {
  if (!value || value.length < 4) return '••••'
  return '••••••••' + value.slice(-4)
}

export class ConfigManager extends EventEmitter {
  private static instance: ConfigManager
  private config: RemoteControlConfig
  private configPath: string

  private constructor() {
    super()
    this.config = { ...DEFAULT_CONFIG }
    this.configPath = path.join(process.cwd(), 'remote-control', 'data', 'config.json')
  }

  private loaded = false

  static getInstance(): ConfigManager {
    if (!ConfigManager.instance) {
      ConfigManager.instance = new ConfigManager()
    }
    return ConfigManager.instance
  }

  async ensureLoaded(): Promise<void> {
    if (this.loaded) return
    await this.load()
    // 首次加载后，执行一次和保存配置相同的逻辑：启动已启用的 bot
    void reconnectAllPlatforms(this.config)
  }

  async load(): Promise<void> {
    try {
      const data = await fs.readFile(this.configPath, 'utf-8')
      this.config = { ...DEFAULT_CONFIG, ...JSON.parse(data) }
    } catch (error) {
      this.config = { ...DEFAULT_CONFIG }
      await this.save()
    }
    this.loaded = true
  }

  private async save(): Promise<void> {
    const dir = path.dirname(this.configPath)
    await fs.mkdir(dir, { recursive: true })
    await fs.writeFile(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8')
  }

  get(): RemoteControlConfig {
    return { ...this.config }
  }

  getMasked(): RemoteControlConfig {
    return {
      discord: {
        ...this.config.discord,
        botToken: maskValue(this.config.discord.botToken),
      },
      dingtalk: {
        ...this.config.dingtalk,
        clientSecret: maskValue(this.config.dingtalk.clientSecret),
      },
      lark: {
        ...this.config.lark,
        appSecret: maskValue(this.config.lark.appSecret),
      },
      telegram: {
        ...this.config.telegram,
        botToken: maskValue(this.config.telegram.botToken),
      },
      slack: {
        ...this.config.slack,
        botToken: maskValue(this.config.slack.botToken),
        appToken: maskValue(this.config.slack.appToken),
      },
    }
  }

  async update(newConfig: Partial<RemoteControlConfig>, options?: { skipEmit?: boolean }): Promise<void> {
    const previousConfig = { ...this.config }

    if (newConfig.discord) {
      this.config.discord = { ...this.config.discord, ...newConfig.discord }
    }
    if (newConfig.dingtalk) {
      this.config.dingtalk = { ...this.config.dingtalk, ...newConfig.dingtalk }
    }
    if (newConfig.lark) {
      this.config.lark = { ...this.config.lark, ...newConfig.lark }
    }
    if (newConfig.telegram) {
      this.config.telegram = { ...this.config.telegram, ...newConfig.telegram }
    }
    if (newConfig.slack) {
      this.config.slack = { ...this.config.slack, ...newConfig.slack }
    }

    await this.save()

    if (!options?.skipEmit) {
      const changedPlatforms = this.getChangedPlatforms(previousConfig, this.config)

      this.emit('config-changed', {
        previousConfig,
        newConfig: this.config,
        changedPlatforms,
      } as ConfigChangeEvent)
    }
  }

  private getChangedPlatforms(
    prev: RemoteControlConfig,
    next: RemoteControlConfig
  ): ('discord' | 'dingtalk' | 'lark' | 'telegram' | 'slack')[] {
    const changed: ('discord' | 'dingtalk' | 'lark' | 'telegram' | 'slack')[] = []

    if (JSON.stringify(prev.discord) !== JSON.stringify(next.discord)) {
      changed.push('discord')
    }
    if (JSON.stringify(prev.dingtalk) !== JSON.stringify(next.dingtalk)) {
      changed.push('dingtalk')
    }
    if (JSON.stringify(prev.lark) !== JSON.stringify(next.lark)) {
      changed.push('lark')
    }
    if (JSON.stringify(prev.telegram) !== JSON.stringify(next.telegram)) {
      changed.push('telegram')
    }
    if (JSON.stringify(prev.slack) !== JSON.stringify(next.slack)) {
      changed.push('slack')
    }

    return changed
  }
}

/**
 * 停止所有 bot，然后启动已启用的 bot。
 * 服务启动和保存配置时复用同一逻辑。
 */
export async function reconnectAllPlatforms(
  config: RemoteControlConfig,
  errors?: string[],
): Promise<void> {
  // Discord
  try {
    const bot = await import('@/remote-control/bots/discord')
    await bot.stopBot()
    if (config.discord.enabled && config.discord.botToken) {
      await bot.startBot(config.discord.botToken)
    }
  } catch (err) {
    if (config.discord.enabled) errors?.push(`Discord: ${err instanceof Error ? err.message : String(err)}`)
  }

  // DingTalk
  try {
    const bot = await import('@/remote-control/bots/dingtalk')
    await bot.stopBot()
    if (config.dingtalk.enabled && config.dingtalk.clientId && config.dingtalk.clientSecret) {
      await bot.startBot(config.dingtalk.clientId, config.dingtalk.clientSecret)
    }
  } catch (err) {
    if (config.dingtalk.enabled) errors?.push(`钉钉: ${err instanceof Error ? err.message : String(err)}`)
  }

  // Lark
  try {
    const bot = await import('@/remote-control/bots/lark')
    await bot.stopBot()
    if (config.lark.enabled && config.lark.appId && config.lark.appSecret) {
      await bot.startBot(config.lark.appId, config.lark.appSecret)
    }
  } catch (err) {
    if (config.lark.enabled) errors?.push(`飞书: ${err instanceof Error ? err.message : String(err)}`)
  }

  // Telegram
  try {
    const bot = await import('@/remote-control/bots/telegram')
    await bot.stopBot()
    if (config.telegram.enabled && config.telegram.botToken) {
      await bot.startBot(config.telegram.botToken)
    }
  } catch (err) {
    if (config.telegram.enabled) errors?.push(`Telegram: ${err instanceof Error ? err.message : String(err)}`)
  }

  // Slack
  try {
    const bot = await import('@/remote-control/bots/slack')
    await bot.stopBot()
    if (config.slack.enabled && config.slack.botToken && config.slack.appToken) {
      await bot.startBot(config.slack.botToken, config.slack.appToken)
    }
  } catch (err) {
    if (config.slack.enabled) errors?.push(`Slack: ${err instanceof Error ? err.message : String(err)}`)
  }
}

export type { RemoteControlConfig, ConfigChangeEvent } from './types'
