import fs from "fs";
import path from "path";
import input from "input";
import { TelegramClient } from "telegram";
import { StringSession } from "telegram/sessions/index.js";
import { config } from "../config.js";

if (!config.telegramApiId || !config.telegramApiHash) {
  throw new Error(
    "TELEGRAM_API_ID o TELEGRAM_API_HASH no están configurados en .env"
  );
}

const client = new TelegramClient(
  new StringSession(config.telegramSession),
  config.telegramApiId,
  config.telegramApiHash,
  { connectionRetries: 5 }
);

let connected = false;

async function connect() {
  if (!connected) {
    await client.connect();
    connected = true;
  }
  return client;
}

function saveSession() {
  const sessionString = client.session.save();
  const envPath = path.join(config.projectRoot, ".env");

  let env = fs.existsSync(envPath)
    ? fs.readFileSync(envPath, "utf8")
    : "";

  if (/^TELEGRAM_SESSION=/m.test(env)) {
    env = env.replace(
      /^TELEGRAM_SESSION=.*$/m,
      `TELEGRAM_SESSION=${sessionString}`
    );
  } else {
    env += `\nTELEGRAM_SESSION=${sessionString}\n`;
  }

  fs.writeFileSync(envPath, env, "utf8");
  console.log("Sesión de Telegram guardada.");
}

export async function ensureTelegramAuth() {
  const tg = await connect();

  if (!await tg.checkAuthorization()) {
    await tg.start({
      phoneNumber: async () => input.text("Número de teléfono: "),
      password: async () => input.text("Contraseña 2FA: "),
      phoneCode: async () => input.text("Código de Telegram: "),
      onError: err => console.error("Error de Telegram:", err)
    });

    saveSession();
  }

  return tg;
}

export async function listDialogs() {
  const tg = await ensureTelegramAuth();
  const dialogs = await tg.getDialogs({ limit: 300 });

  return dialogs
    .filter(d => Boolean(d.entity))
    .map(d => ({
      id: String(d.id),
      title: d.title || d.name || "Sin nombre",
      username: d.entity?.username || null,
      type: d.entity?.broadcast
        ? "channel"
        : d.entity?.megagroup
          ? "group"
          : "chat",
      unreadCount: Number(d.unreadCount || 0)
    }));
}

export async function getMessageById(chatId, messageId) {
  const tg = await ensureTelegramAuth();
  const entity = await tg.getEntity(chatId);
  const messages = await tg.getMessages(entity, {
    ids: [Number(messageId)]
  });

  return messages?.[0] || null;
}

export async function findPreviewPhoto(chatId, message, radius = 4) {
  const tg = await ensureTelegramAuth();
  const entity = await tg.getEntity(chatId);

  // Same album/media-group is the safest association.
  if (message?.groupedId) {
    const start = Math.max(1, Number(message.id) - radius);
    const end = Number(message.id) + radius;
    const ids = [];

    for (let id = start; id <= end; id++) ids.push(id);

    const nearby = await tg.getMessages(entity, { ids });

    const sameGroupPhoto = nearby
      .filter(Boolean)
      .find(item =>
        item.photo &&
        String(item.groupedId || "") === String(message.groupedId)
      );

    if (sameGroupPhoto) return sameGroupPhoto;
  }

  // Fallback for common channel layouts where a preview photo is posted
  // immediately beside the archive/document.
  const start = Math.max(1, Number(message.id) - radius);
  const end = Number(message.id) + radius;
  const ids = [];

  for (let id = start; id <= end; id++) ids.push(id);

  const nearby = await tg.getMessages(entity, { ids });

  const photos = nearby
    .filter(item => item && item.photo)
    .sort(
      (a, b) =>
        Math.abs(Number(a.id) - Number(message.id)) -
        Math.abs(Number(b.id) - Number(message.id))
    );

  return photos[0] || null;
}

export async function downloadPreviewToFile(
  message,
  outputPath
) {
  const tg = await ensureTelegramAuth();

  try {
    const result = await tg.downloadMedia(
      message,
      {
        thumb: 0,
        outputFile: outputPath
      }
    );

    return result;
  } catch (error) {
    console.warn(
      "No se pudo descargar la miniatura:",
      error.message
    );
    return null;
  }
}

export async function streamMessageToResponse(
  message,
  res,
  filename,
  mimeType,
  fileSize
) {
  const tg = await ensureTelegramAuth();

  if (!message?.media) {
    throw new Error("El mensaje no contiene media descargable.");
  }

  res.statusCode = 200;
  res.setHeader(
    "Content-Type",
    mimeType || "application/octet-stream"
  );
  res.setHeader(
    "Content-Disposition",
    `attachment; filename*=UTF-8''${encodeURIComponent(filename || "modelo")}`
  );

  if (fileSize && Number.isFinite(Number(fileSize))) {
    res.setHeader(
      "Content-Length",
      String(fileSize)
    );
  }

  const iterator = tg.iterDownload({
    file: message.media,
    requestSize: 512 * 1024,
    chunkSize: 512 * 1024,
    fileSize: Number(fileSize || 0)
  });

  try {
    for await (const chunk of iterator) {
      if (res.destroyed) {
        return;
      }

      if (!res.write(chunk)) {
        await new Promise(resolve => {
          res.once("drain", resolve);
        });
      }
    }

    res.end();
  } catch (error) {
    console.error(
      "STREAM DOWNLOAD ERROR:",
      error
    );

    if (!res.headersSent) {
      throw error;
    }

    res.destroy(error);
  }
}
