/**
 * Auto paired-mode degrade/restore.
 *
 * When all configured Claude accounts are at/over the 95% exhaustion threshold,
 * paired (tribunal) rooms cannot meaningfully run their reviewer/arbiter
 * because the gate blocks Claude turns. To keep the user-facing rooms
 * responsive, we temporarily flip selected rooms to `single` mode and notify
 * each affected channel. When usage recovers we flip them back to `tribunal`.
 *
 * Selection (Option B agreed with the user):
 * - The most-recently-active room (per chats.last_message_time)
 * - All rooms that are currently in-flight (queue status === 'processing')
 * - Deduplicated; rooms whose owner is `claude-code` are skipped because
 *   degrading them does not help — the 95% gate blocks them either way.
 *
 * Restore behavior:
 * - Only restore a room if it is still in 'single' (don't override a manual
 *   user change made during the degrade window).
 */
import fs from 'fs';
import path from 'path';

import { getClaudeUsageExhaustion } from './claude-usage.js';
import { DATA_DIR } from './config.js';
import {
  getAllChats,
  getAllRoomBindings,
  getEffectiveRoomMode,
  getStoredRoomSettings,
  setExplicitRoomMode,
} from './db.js';
import { logger } from './logger.js';
import { readJsonFile, writeJsonFile } from './utils.js';

const STATE_PATH = path.join(DATA_DIR, 'auto-paired-mode-state.json');

const DEGRADE_NOTICE =
  '클로드 사용량이 95%를 넘어 잠시 single 모드로 전환했어요. 사용량이 회복되면 paired 모드로 다시 켤게요.';
const RESTORE_NOTICE = '클로드 사용량이 회복돼서 paired 모드로 다시 켰어요.';

interface DowngradeRecord {
  chatJid: string;
  atIso: string;
}

interface AutoPairedModeState {
  exhausted: boolean;
  downgraded: DowngradeRecord[];
}

export interface AutoPairedModeDeps {
  queue?: {
    getStatuses(jids: string[]): Array<{ jid: string; status: string }>;
  };
  sendNotice: (chatJid: string, text: string) => Promise<void>;
}

function loadState(): AutoPairedModeState {
  const raw = readJsonFile<AutoPairedModeState>(STATE_PATH);
  if (!raw || typeof raw !== 'object') {
    return { exhausted: false, downgraded: [] };
  }
  return {
    exhausted: Boolean(raw.exhausted),
    downgraded: Array.isArray(raw.downgraded) ? raw.downgraded : [],
  };
}

function saveState(state: AutoPairedModeState): void {
  try {
    fs.mkdirSync(path.dirname(STATE_PATH), { recursive: true });
    writeJsonFile(STATE_PATH, state);
  } catch (err) {
    logger.warn({ err }, 'auto-paired-mode: failed to persist state');
  }
}

function pickCandidates(deps: AutoPairedModeDeps): string[] {
  const bindings = getAllRoomBindings();
  const allJids = Object.keys(bindings);
  if (allJids.length === 0) return [];

  const candidates = new Set<string>();

  // Most-recently-active room first (chats ordered DESC by last_message_time).
  const chats = getAllChats();
  const recent = chats.find((c) => allJids.includes(c.jid));
  if (recent) candidates.add(recent.jid);

  // Currently in-flight rooms.
  if (deps.queue) {
    for (const status of deps.queue.getStatuses(allJids)) {
      if (status.status === 'processing') candidates.add(status.jid);
    }
  }

  // Skip claude-owned rooms (Option B).
  const result: string[] = [];
  for (const jid of candidates) {
    const settings = getStoredRoomSettings(jid);
    if (settings?.ownerAgentType === 'claude-code') continue;
    if (getEffectiveRoomMode(jid) !== 'tribunal') continue;
    result.push(jid);
  }
  return result;
}

export async function evaluateAndApplyAutoPairedMode(
  deps: AutoPairedModeDeps,
): Promise<void> {
  const exhaustion = getClaudeUsageExhaustion();
  const state = loadState();

  if (exhaustion.exhausted && !state.exhausted) {
    // Transition: healthy → exhausted. Downgrade selected rooms.
    const targets = pickCandidates(deps);
    const downgraded: DowngradeRecord[] = [];
    for (const jid of targets) {
      try {
        setExplicitRoomMode(jid, 'single');
        downgraded.push({ chatJid: jid, atIso: new Date().toISOString() });
        logger.info(
          { jid },
          'auto-paired-mode: downgraded room to single (claude exhausted)',
        );
        try {
          await deps.sendNotice(jid, DEGRADE_NOTICE);
        } catch (err) {
          logger.warn(
            { err, jid },
            'auto-paired-mode: failed to send degrade notice',
          );
        }
      } catch (err) {
        logger.warn({ err, jid }, 'auto-paired-mode: failed to downgrade room');
      }
    }
    saveState({ exhausted: true, downgraded });
    return;
  }

  if (!exhaustion.exhausted && state.exhausted) {
    // Transition: exhausted → healthy. Restore previously-downgraded rooms.
    for (const record of state.downgraded) {
      try {
        if (getEffectiveRoomMode(record.chatJid) !== 'single') {
          // Respect manual user changes during the degrade window.
          logger.info(
            { jid: record.chatJid },
            'auto-paired-mode: skip restore (room is no longer single)',
          );
          continue;
        }
        setExplicitRoomMode(record.chatJid, 'tribunal');
        logger.info(
          { jid: record.chatJid },
          'auto-paired-mode: restored room to tribunal',
        );
        try {
          await deps.sendNotice(record.chatJid, RESTORE_NOTICE);
        } catch (err) {
          logger.warn(
            { err, jid: record.chatJid },
            'auto-paired-mode: failed to send restore notice',
          );
        }
      } catch (err) {
        logger.warn(
          { err, jid: record.chatJid },
          'auto-paired-mode: failed to restore room',
        );
      }
    }
    saveState({ exhausted: false, downgraded: [] });
    return;
  }

  // Steady state — nothing to do.
}
