import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const AUTH_FILE = path.join(__dirname, 'telegram_authorized_users.json');
const BOT_TOKEN = process.env.WATER_VALVE_TELEGRAM_TOKEN;
const BOT_PASSWORD = process.env.WATER_VALVE_TELEGRAM_PASSWORD;
const TELEGRAM_API = `https://api.telegram.org/bot${BOT_TOKEN}`;
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));


// Generation counter — incremented on each hot reload to stop the old poll loop
const BOT_GEN = Date.now();
if (!global.__waterBotGen) global.__waterBotGen = 0;
global.__waterBotGen = BOT_GEN;

export function startTelegramBot({
  getState,
  WATER_BUTTON_MAC,
  REMOTE_SWITCH_MAC,
  setLight,
  persistedState,
  saveState
}) {
  // ── Telegram auth ─────────────────────────────────────────────────────────────

  function loadAuthorizedUsers() {
    try {
      if (fs.existsSync(AUTH_FILE)) {
        return new Set(JSON.parse(fs.readFileSync(AUTH_FILE, 'utf8')));
      }
    } catch (err) {
      console.error('[TelegramBot] Error loading authorized users:', err);
    }
    return new Set();
  }

  function saveAuthorizedUsers(users) {
    try {
      fs.writeFileSync(AUTH_FILE, JSON.stringify([...users], null, 2));
    } catch (err) {
      console.error('[TelegramBot] Error saving authorized users:', err);
    }
  }

  const authorizedUsers = loadAuthorizedUsers();

  // ── Telegram bot ──────────────────────────────────────────────────────────────

  async function telegramRequest(method, params = {}) {
    const res = await fetch(`${TELEGRAM_API}/${method}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(params)
    });
    return res.json();
  }

  function menuKeyboard(durSec) {
    return {
      keyboard: [
        [{ text: '🔓 Open valve' }, { text: '🔒 Close valve' }],
        [{ text: `💧 Run timer (${durSec}s)` }, { text: '📊 Status' }]
      ],
      resize_keyboard: true,
      persistent: true
    };
  }

  async function getConnectionStatus() {
    try {
      const getState = global.__waterBotGetState;
      if (!getState) return '❓ Button\n❓ Valve';
      const [buttonOnline, valveOnline] = await Promise.all([
        getState(WATER_BUTTON_MAC, 'system', 'online'),
        getState(REMOTE_SWITCH_MAC, 'system', 'online')
      ]);
      const btn = buttonOnline === true ? '✅' : buttonOnline === false ? '❌ OFFLINE' : '❓';
      const vlv = valveOnline === true ? '✅' : valveOnline === false ? '❌ OFFLINE' : '❓';
      return `${btn} Button\n${vlv} Valve`;
    } catch {
      return '❓ Button\n❓ Valve';
    }
  }

  async function reply(chatId, text, durSec) {
    const connStatus = await getConnectionStatus();
    return telegramRequest('sendMessage', {
      chat_id: chatId,
      text: `${connStatus}\n\n${text}`,
      reply_markup: menuKeyboard(durSec)
    });
  }

  async function sendMessage(chatId, text) {
    return telegramRequest('sendMessage', { chat_id: chatId, text });
  }

  function parseDuration(raw) {
    if (!raw) return null;
    if (raw.endsWith('m')) return Math.round(parseFloat(raw) * 60000);
    if (raw.endsWith('s')) return Math.round(parseFloat(raw) * 1000);
    const ms = parseInt(raw, 10);
    return isNaN(ms) ? null : ms;
  }

  async function handleBotMessage(msg) {
    const chatId = msg.chat.id;
    const text = (msg.text || '').trim();
    const parts = text.split(/\s+/);
    // Strip @botname suffix that Telegram adds in groups
    const cmd = parts[0].toLowerCase().split('@')[0];

    // ── Authorization gate ───────────────────────────────────────────────────
    if (!authorizedUsers.has(chatId)) {
      if (text === BOT_PASSWORD) {
        authorizedUsers.add(chatId);
        saveAuthorizedUsers(authorizedUsers);
        console.log(`[TelegramBot] New authorized user: ${chatId} (${msg.from?.username || msg.from?.first_name || 'unknown'})`);
        const state = getState(WATER_BUTTON_MAC);
        await reply(chatId, 'Access granted. Use the buttons below or type a number (seconds) to set the duration.', (state.storedDuration / 1000).toFixed(1));
      } else {
        await sendMessage(chatId, 'Please enter the password to use this bot.');
      }
      return;
    }

    const state = getState(WATER_BUTTON_MAC);
    const durSec = (state.storedDuration / 1000).toFixed(1);

    // ── Plain number → set duration in seconds ───────────────────────────────
    const numericMatch = text.match(/^(\d+(?:\.\d+)?)$/);
    if (numericMatch) {
      const ms = Math.round(parseFloat(numericMatch[1]) * 1000);
      if (ms <= 0 || ms > 300000) {
        await reply(chatId, 'Duration must be between 1 and 300 seconds.', durSec);
        return;
      }
      state.storedDuration = ms;
      persistedState[WATER_BUTTON_MAC] = { storedDuration: ms };
      saveState(persistedState);
      const newSec = (ms / 1000).toFixed(1);
      await reply(chatId, `Duration set to ${newSec}s.`, newSec);
      return;
    }

    // ── Menu button labels map to commands ───────────────────────────────────
    const buttonMap = {
      '🔓 open valve': '/open',
      '🔒 close valve': '/close',
      '📊 status': '/status'
    };
    // Run timer button label includes dynamic duration — match by prefix
    const normalised = text.toLowerCase();
    const resolvedCmd = buttonMap[normalised]
      || (normalised.startsWith('💧 run timer') ? '/run' : cmd);

    if (resolvedCmd === '/start' || resolvedCmd === '/help') {
      await reply(chatId,
        `💧 Water valve bot\n\n` +
        `Use the buttons below, or:\n` +
        `/open — Open valve\n` +
        `/close — Close valve\n` +
        `/run — Open then close after ${durSec}s\n` +
        `/duration <value> — Set timer (e.g. 30s, 2m, 10000)\n` +
        `<number> — Set duration in seconds (e.g. 45)\n` +
        `/status — Current status`,
        durSec
      );
      return;
    }

    if (resolvedCmd === '/status') {
      await reply(chatId,
        `Status:\n` +
        `• Duration: ${state.storedDuration}ms (${durSec}s)\n` +
        `• Count mode: ${state.countMode}\n` +
        `• Timer active: ${state.timer !== null}`,
        durSec
      );
      return;
    }

    if (resolvedCmd === '/duration') {
      if (!parts[1]) {
        await reply(chatId,
          `Current duration: ${durSec}s\n` +
          `Type a number (seconds) or use /duration <value>\n` +
          `Examples: 30  /duration 30s  /duration 2m`,
          durSec
        );
        return;
      }
      const ms = parseDuration(parts[1]);
      if (!ms || ms <= 0 || ms > 300000) {
        await reply(chatId, 'Invalid duration. Use e.g. 30s, 2m, or seconds 1–300.', durSec);
        return;
      }
      state.storedDuration = ms;
      persistedState[WATER_BUTTON_MAC] = { storedDuration: ms };
      saveState(persistedState);
      const newSec = (ms / 1000).toFixed(1);
      await reply(chatId, `Duration set to ${newSec}s.`, newSec);
      return;
    }

    const sendRPC = global.__waterBotRPC;
    if (!sendRPC) {
      await reply(chatId, 'Not ready yet — waiting for first device event. Try again in a moment.', durSec);
      return;
    }

    if (resolvedCmd === '/open') {
      if (state.timer) { clearTimeout(state.timer); state.timer = null; }
      state.countMode = false;
      state.timerStartedAt = null;
      state.timerEndsAt = null;
      await setLight(sendRPC, WATER_BUTTON_MAC, false, 0);  // light off = switch on = valve open
      await reply(chatId, 'Valve opened.', durSec);

    } else if (resolvedCmd === '/close') {
      if (state.timer) { clearTimeout(state.timer); state.timer = null; }
      state.countMode = false;
      state.timerStartedAt = null;
      state.timerEndsAt = null;
      await setLight(sendRPC, WATER_BUTTON_MAC, true, 95);  // light on = switch off = valve closed
      await reply(chatId, 'Valve closed.', durSec);

    } else if (resolvedCmd === '/run') {
      if (state.timer) { clearTimeout(state.timer); state.timer = null; }
      state.countMode = false;
      await setLight(sendRPC, WATER_BUTTON_MAC, false, 0);  // open valve
      const duration = state.storedDuration;
      state.timerStartedAt = Date.now();
      state.timerEndsAt = Date.now() + duration;
      await reply(chatId, `Valve opened. Closing in ${(duration / 1000).toFixed(1)}s…`, durSec);

      state.timer = setTimeout(async () => {
        try {
          const rpc = global.__waterBotRPC;
          if (rpc) await setLight(rpc, WATER_BUTTON_MAC, true, 95);  // close valve
          state.timer = null;
          state.timerStartedAt = null;
          state.timerEndsAt = null;
          await reply(chatId, 'Timer elapsed — valve closed.', (getState(WATER_BUTTON_MAC).storedDuration / 1000).toFixed(1));
        } catch (err) {
          console.error('[TelegramBot] Timer callback error:', err);
        }
      }, duration);

    } else {
      await reply(chatId, 'Unknown command. Use the buttons below or send /help.', durSec);
    }
  }

  async function botLoop() {
    console.log(`[TelegramBot] Starting (gen ${BOT_GEN})`);
    let offset = 0;

    while (BOT_GEN === global.__waterBotGen) {
      try {
        const data = await telegramRequest('getUpdates', {
          offset,
          timeout: 25,
          allowed_updates: ['message']
        });

        if (!data.ok) {
          await sleep(5000);
          continue;
        }

        for (const update of data.result || []) {
          offset = update.update_id + 1;
          if (update.message) {
            handleBotMessage(update.message).catch(err =>
              console.error('[TelegramBot] Handler error:', err)
            );
          }
        }
      } catch (err) {
        console.error('[TelegramBot] Polling error:', err.message);
        await sleep(5000);
      }
    }

    console.log(`[TelegramBot] Stopped (gen ${BOT_GEN} superseded)`);
  }

  // Start long-polling immediately (non-blocking)
  if (BOT_TOKEN) {
    botLoop().catch(err => console.error('[TelegramBot] Fatal error:', err));
  } else {
    console.error('[TelegramBot] WATER_VALVE_TELEGRAM_TOKEN not set in .env — bot disabled');
  }


}
