import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import {
  clearConfiguredOfficialChannelToken,
  clearConfiguredOfficialChannelTokensEverywhere,
  getOfficialChannelTokenStatus,
  getOfficialChannelEnvPath,
  hasConfiguredOfficialChannelToken,
  readConfiguredOfficialChannelToken,
  readOfficialChannelTokenFromProcessEnv,
  readOfficialChannelTokenFromEnvContent,
  setConfiguredOfficialChannelToken,
} from '../../../src/channels/official-channels-store';

describe('official channels token store', () => {
  let tempHome = '';
  let originalHome: string | undefined;
  let originalCcsHome: string | undefined;

  beforeEach(() => {
    tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-discord-channels-'));
    originalHome = process.env.HOME;
    originalCcsHome = process.env.CCS_HOME;
    process.env.HOME = tempHome;
    process.env.CCS_HOME = tempHome;
  });

  afterEach(() => {
    if (originalHome !== undefined) process.env.HOME = originalHome;
    else delete process.env.HOME;

    if (originalCcsHome !== undefined) process.env.CCS_HOME = originalCcsHome;
    else delete process.env.CCS_HOME;

    fs.rmSync(tempHome, { recursive: true, force: true });
  });

  it('writes and reads DISCORD_BOT_TOKEN from the canonical Discord env file', () => {
    const envPath = setConfiguredOfficialChannelToken('discord', 'discord-secret');

    expect(envPath).toBe(path.join(tempHome, '.claude', 'channels', 'discord', '.env'));
    expect(hasConfiguredOfficialChannelToken('discord')).toBe(true);
    expect(readConfiguredOfficialChannelToken('discord')).toBe('discord-secret');
    expect(readOfficialChannelTokenFromEnvContent('discord', fs.readFileSync(envPath, 'utf8'))).toBe(
      'discord-secret'
    );
  });

  it('writes and reads TELEGRAM_BOT_TOKEN from the canonical Telegram env file', () => {
    const envPath = setConfiguredOfficialChannelToken('telegram', 'telegram-secret');

    expect(envPath).toBe(path.join(tempHome, '.claude', 'channels', 'telegram', '.env'));
    expect(hasConfiguredOfficialChannelToken('telegram')).toBe(true);
    expect(readConfiguredOfficialChannelToken('telegram')).toBe('telegram-secret');
  });

  it('rejects oversized official channel tokens before writing channel state', () => {
    const envPath = getOfficialChannelEnvPath('discord');

    expect(() => setConfiguredOfficialChannelToken('discord', 'x'.repeat(4097))).toThrow(
      'DISCORD_BOT_TOKEN cannot exceed 4096 characters.'
    );
    expect(fs.existsSync(envPath)).toBe(false);
    expect(hasConfiguredOfficialChannelToken('discord')).toBe(false);
  });

  it('uses the official state-dir override when one is configured', () => {
    const originalDiscordStateDir = process.env.DISCORD_STATE_DIR;
    process.env.DISCORD_STATE_DIR = path.join(tempHome, 'discord-state');

    try {
      const envPath = setConfiguredOfficialChannelToken('discord', 'discord-secret');

      expect(envPath).toBe(path.join(tempHome, 'discord-state', '.env'));
      expect(getOfficialChannelEnvPath('discord')).toBe(path.join(tempHome, 'discord-state', '.env'));
      expect(readConfiguredOfficialChannelToken('discord')).toBe('discord-secret');
    } finally {
      if (originalDiscordStateDir !== undefined) {
        process.env.DISCORD_STATE_DIR = originalDiscordStateDir;
      } else {
        delete process.env.DISCORD_STATE_DIR;
      }
    }
  });

  it('treats a current-process env token as available readiness without marking it as saved', () => {
    const originalDiscordToken = process.env.DISCORD_BOT_TOKEN;
    process.env.DISCORD_BOT_TOKEN = 'discord-from-env';

    try {
      expect(readOfficialChannelTokenFromProcessEnv('discord')).toBe('discord-from-env');
      expect(hasConfiguredOfficialChannelToken('discord')).toBe(false);
      expect(getOfficialChannelTokenStatus('discord')).toEqual({
        available: true,
        source: 'process_env',
        envKey: 'DISCORD_BOT_TOKEN',
        savedInClaudeState: false,
        processEnvAvailable: true,
      });
    } finally {
      if (originalDiscordToken !== undefined) {
        process.env.DISCORD_BOT_TOKEN = originalDiscordToken;
      } else {
        delete process.env.DISCORD_BOT_TOKEN;
      }
    }
  });

  it('prefers current-process env tokens over saved Claude state for readiness source', () => {
    const originalTelegramToken = process.env.TELEGRAM_BOT_TOKEN;
    setConfiguredOfficialChannelToken('telegram', 'telegram-saved');
    process.env.TELEGRAM_BOT_TOKEN = 'telegram-from-env';

    try {
      expect(getOfficialChannelTokenStatus('telegram')).toEqual({
        available: true,
        source: 'saved_env',
        envKey: 'TELEGRAM_BOT_TOKEN',
        tokenPath: path.join(tempHome, '.claude', 'channels', 'telegram', '.env'),
        savedInClaudeState: true,
        processEnvAvailable: true,
      });
    } finally {
      if (originalTelegramToken !== undefined) {
        process.env.TELEGRAM_BOT_TOKEN = originalTelegramToken;
      } else {
        delete process.env.TELEGRAM_BOT_TOKEN;
      }
    }
  });

  it('removes only the channel token entry and deletes the file when nothing remains', () => {
    const envPath = getOfficialChannelEnvPath('discord');
    fs.mkdirSync(path.dirname(envPath), { recursive: true });
    fs.writeFileSync(envPath, '# comment\nDISCORD_BOT_TOKEN=secret\nOTHER_KEY=value\n', 'utf8');

    clearConfiguredOfficialChannelToken('discord');
    expect(fs.readFileSync(envPath, 'utf8')).toBe('# comment\nOTHER_KEY=value\n');

    clearConfiguredOfficialChannelToken('discord');
    fs.writeFileSync(envPath, 'DISCORD_BOT_TOKEN=secret\n', 'utf8');
    clearConfiguredOfficialChannelToken('discord');
    expect(fs.existsSync(envPath)).toBe(false);
  });

  it('clears previously synced copies across managed Claude config dirs', () => {
    const originalClaudeConfigDir = process.env.CLAUDE_CONFIG_DIR;
    setConfiguredOfficialChannelToken('discord', 'discord-secret');
    setConfiguredOfficialChannelToken('telegram', 'telegram-secret');

    const instanceConfigDir = path.join(tempHome, '.ccs', 'instances', 'work');
    const processConfigDir = path.join(tempHome, '.claude-account-session');
    const staleDiscordInstancePath = getOfficialChannelEnvPath('discord', instanceConfigDir);
    const staleTelegramInstancePath = getOfficialChannelEnvPath('telegram', instanceConfigDir);
    const staleDiscordProcessPath = getOfficialChannelEnvPath('discord', processConfigDir);

    process.env.CLAUDE_CONFIG_DIR = processConfigDir;

    try {
      fs.mkdirSync(path.dirname(staleDiscordInstancePath), { recursive: true });
      fs.writeFileSync(staleDiscordInstancePath, 'DISCORD_BOT_TOKEN=discord-secret\n', 'utf8');

      fs.mkdirSync(path.dirname(staleTelegramInstancePath), { recursive: true });
      fs.writeFileSync(staleTelegramInstancePath, 'TELEGRAM_BOT_TOKEN=telegram-secret\n', 'utf8');

      fs.mkdirSync(path.dirname(staleDiscordProcessPath), { recursive: true });
      fs.writeFileSync(staleDiscordProcessPath, 'DISCORD_BOT_TOKEN=discord-secret\n', 'utf8');

      expect(fs.existsSync(staleDiscordInstancePath)).toBe(true);
      expect(fs.existsSync(staleTelegramInstancePath)).toBe(true);
      expect(fs.existsSync(staleDiscordProcessPath)).toBe(true);

      const clearedPaths = clearConfiguredOfficialChannelTokensEverywhere();

      expect(clearedPaths).toContain(getOfficialChannelEnvPath('discord'));
      expect(clearedPaths).toContain(getOfficialChannelEnvPath('telegram'));
      expect(clearedPaths).toContain(staleDiscordInstancePath);
      expect(clearedPaths).toContain(staleTelegramInstancePath);
      expect(clearedPaths).toContain(staleDiscordProcessPath);
      expect(fs.existsSync(getOfficialChannelEnvPath('discord'))).toBe(false);
      expect(fs.existsSync(getOfficialChannelEnvPath('telegram'))).toBe(false);
      expect(fs.existsSync(staleDiscordInstancePath)).toBe(false);
      expect(fs.existsSync(staleTelegramInstancePath)).toBe(false);
      expect(fs.existsSync(staleDiscordProcessPath)).toBe(false);
    } finally {
      if (originalClaudeConfigDir !== undefined) {
        process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir;
      } else {
        delete process.env.CLAUDE_CONFIG_DIR;
      }
    }
  });
});
