import fetch from 'node-fetch';
import { createHash, createHmac } from 'crypto';
import memoize from 'fast-memoize';
import { Member, telegramTypes, UpdateArchive } from './_types';
import { ANONYMOUS } from './_locale.en';

const appendAuthor = (caption = '', postfix = '', createdAt = '') => {
  let response = '';

  if (postfix) {
    response = caption ? `${caption}\n\n${postfix}` : postfix;
  } else {
    response = caption || '';
  }

  // TODO: think about timezone submissions
  if (createdAt) {
    // response = `${response}\n\n${createdAt}`;
  }

  // captions & texts
  const maxLengths = [1024, 4096];
  const captionLength = caption.length;

  if (maxLengths.includes(captionLength) && !caption.includes(postfix)) {
    response = appendAuthor(
      caption.slice(0, captionLength - (response.length - captionLength)),
      postfix,
      createdAt
    );
  }

  return response;
};

const draftBody = (
  postfix: string,
  chat_id: number,
  reply_to_message_id: number = null,
  disable_notification: boolean = false,
  theUpdate?: UpdateArchive
) => {
  const body = theUpdate?.body || {};
  const type = theUpdate?.type || 'text';
  const file_id = theUpdate?.file_id || '';

  let data = {
    reply_to_message_id,
    chat_id,
    disable_notification,
    caption: appendAuthor(
      body?.message?.caption,
      postfix,
      theUpdate?.createdAt
    ),
    entities: body?.message?.entities,
    media: file_id || [],
    caption_entities: body?.message?.caption_entities,
    [type]: file_id || postfix,
    type,
  };

  if (type === 'poll') {
    data = {
      ...data,
      ...body?.message?.[type],
      options: body?.message?.[type]?.options?.map((o) => o?.text),
    };
  } else if (type === 'photo') {
    data = {
      ...data,
      [type]: body?.message?.[type].slice(-1)[0].file_id,
    };
  } else if (type === 'text' && body?.message?.[type]) {
    data = {
      ...data,
      [type]: appendAuthor(body.message[type], postfix, theUpdate?.createdAt),
    };
  }

  return data;
};

const sent = {};
export const sendMsg = async (
  postfix: string,
  chat_id: number,
  reply_to_message_id: number = null,
  disable_notification: boolean = false,
  theUpdate?: UpdateArchive,
  allUpdates?: UpdateArchive[]
) => {
  const body = theUpdate?.body || {};
  const groupId = body?.message?.media_group_id;
  const type = groupId ? 'group' : theUpdate?.type || 'text';
  const apiEndpoint = telegramTypes[type] || telegramTypes.text;

  let data = draftBody(
    postfix,
    chat_id,
    reply_to_message_id,
    disable_notification,
    theUpdate
  );

  if (Array.isArray(sent[chat_id]) && sent[chat_id].includes(groupId)) {
    console.log(groupId, 'already sent');
    return false;
  }

  const sendChatAction = `https://api.telegram.org/bot${process.env.TELEGRAM_API_KEY}/sendChatAction`;

  await fetch(sendChatAction, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id, action: 'typing' }),
  });

  if (type === 'group') {
    sent[chat_id] = Array.isArray(sent[chat_id])
      ? [...sent[chat_id], groupId]
      : [groupId];
    const mediaGroup = allUpdates.filter((update: UpdateArchive) => {
      return update.body?.message?.media_group_id === groupId;
    });

    if (Array.isArray(mediaGroup) && mediaGroup.length) {
      data.media = [];
      mediaGroup.forEach((u, i) => {
        if (Array.isArray(data.media)) {
          data.media.push(
            draftBody(
              !i && postfix,
              chat_id,
              reply_to_message_id,
              disable_notification,
              u
            )
          );
        }
      });
    }
  }

  // Adding chat_id at the end even tho tg ignores it, for our own debugging when an endpoint fails in send.ts
  const url = `https://api.telegram.org/bot${process.env.TELEGRAM_API_KEY}/${apiEndpoint}?chat_id=${data?.chat_id}`;

  return fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });
};

const secret = createHash('sha256')
  .update(process.env.TELEGRAM_API_KEY)
  .digest();

const signatureMath = ({ hash, ...data }) => {
  if (!hash) {
    return false;
  }

  const checkString = Object.keys(data)
    .sort()
    .map((k) => `${k}=${data[k]}`)
    .join('\n');
  const hmac = createHmac('sha256', secret).update(checkString).digest('hex');
  return hmac === hash;
};

export const checkSignature = memoize(signatureMath);

export const getSubmissionDates = () => {
  // Releases are at 4pm UTC every day
  const nextSubmit = new Date();
  nextSubmit.setUTCHours(15);
  nextSubmit.setUTCMinutes(8);
  nextSubmit.setUTCSeconds(0);

  // Can manually send a day by setting it here (3 less than today for example)
  // nextSubmit.setUTCDate(nextSubmit.getUTCDate() - 3);

  // Manually send tomorrow's update by doing + 1
  // nextSubmit.setUTCDate(nextSubmit.getUTCDate() + 1);

  const nextSubmitTimestamp = nextSubmit.getTime();
  nextSubmit.setUTCDate(nextSubmit.getUTCDate() - 1);
  const previousSubmitTimestamp = nextSubmit.getTime();

  return { previousSubmitTimestamp, nextSubmitTimestamp };
};

export const getDisplayName = (user: Member) => {
  const { first_name, last_name, username } = user.about;
  const userFullName = `${first_name || ''} ${last_name || ''}`.trim();
  if (userFullName) return userFullName;
  if (username) return username;

  return ANONYMOUS;
};