import * as tg from "@tma.js/sdk-react";
import { useLaunchParams, useSignal } from "@tma.js/sdk-react";
import { prefetch } from "@/helpers/dom";

const USER_MOCK = {
  id: 1,
  first_name: "Pavel",
  is_bot: false,
  last_name: "Durov",
  username: "durov",
  language_code: "en",
  is_premium: true,
  photo_url:
    "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR8QcGzgQKSnte-VdI1k1QUbKwVEAdYhFIzEw&s",
  added_to_attachment_menu: false,
  allows_write_to_pm: true,
} satisfies tg.User;

const CSS_VARS_MOCK = {
  "--tg-viewport-content-safe-area-inset-top": 10,
  "--tg-viewport-safe-area-inset-top": 10,
  "--tg-viewport-content-safe-area-inset-bottom": 10,
  "--tg-viewport-safe-area-inset-bottom": 20,
  "--tg-viewport-content-safe-area-inset-left": 10,
  "--tg-viewport-safe-area-inset-left": 10,
  "--tg-viewport-content-safe-area-inset-right": 10,
  "--tg-viewport-safe-area-inset-right": 10,
};

export const STORAGE_KEYS = {
  authToken: "authToken",
  soundEnabled: "soundEnabled",
} as const;
export type StorageKey = (typeof STORAGE_KEYS)[keyof typeof STORAGE_KEYS];

type TgResult<T> = { ok: true; data: T } | { ok: false };

const isPromise = <T>(value: T | Promise<T>): value is Promise<T> =>
  value && typeof value === "object" && "then" in value && typeof value.then === "function";

const promisify = <T>(value: T | Promise<T>): Promise<T> =>
  isPromise(value) ? value : Promise.resolve(value);

const fallbackImplementation = <
  TMain extends (...args: any) => TgResult<any>,
  Async extends Extract<ReturnType<TMain>, { ok: true }>["data"] extends Promise<any>
    ? true
    : false,
  Params extends Parameters<TMain>,
  Result extends Awaited<Extract<ReturnType<TMain>, { ok: true }>["data"]>,
  TFallback extends (...args: Params) => Async extends true ? Result | Awaited<Result> : Result,
>(
  async: Async,
  args: Params,
  cb: TMain,
  onErr: TFallback,
): Async extends true ? Promise<Result> : Awaited<Result> => {
  const res = cb.apply(null, args);
  const returnValue = (
    res.ok && !import.meta.env.DEV ? res.data : onErr.apply(null, args)
  ) as Result;
  if (!async) return returnValue as Async extends true ? Promise<Result> : Awaited<Result>;
  return promisify(returnValue).catch(() => onErr.apply(null, args)) as Async extends true
    ? Promise<Result>
    : Awaited<Result>;
};

const mockCssVar = (key: string, px: number) =>
  document.documentElement.style.setProperty(key, `${px}px`);

const externalTgApi = {
  init: () => {
    tg.setDebug(import.meta.env.DEV);

    if (import.meta.env.DEV) {
      tg.mockTelegramEnv({
        launchParams: {
          tgWebAppData: new URLSearchParams({
            user: JSON.stringify(USER_MOCK),
            hash: "",
            signature: "",
            auth_date: Date.now().toString(),
          }),
          tgWebAppThemeParams: {},
          tgWebAppVersion: "999999",
          tgWebAppPlatform: "unknown",
        },
      });
      console.log("Telegram Mocked environment initialized");
    }

    const launchParams = tg.retrieveLaunchParams();
    console.log("Lunch params: ", launchParams);
    const isMobile = ["ios", "android"].includes(launchParams.tgWebAppPlatform);

    tg.init();
    console.log("TMA initialized");

    if (launchParams.tgWebAppPlatform === "macos") {
      tg.mockTelegramEnv({
        onEvent(event, next) {
          if (event.name === "web_app_request_safe_area") {
            return tg.emitEvent("safe_area_changed", { left: 0, top: 0, right: 0, bottom: 0 });
          }

          next();
        },
      });
      console.log("MacOS Mocked environment initialized");
    }

    if (tg.miniApp.mount.isAvailable()) {
      tg.themeParams.mount();
      tg.miniApp.mount();
      tg.themeParams.bindCssVars();
      tg.miniApp.bindCssVars();
      tg.miniApp.setHeaderColor("#000000");
      console.log("Telegram Mini App initialized");
    }

    if (tg.viewport.mount.isAvailable()) {
      tg.viewport.mount().then(() => {
        tg.viewport.bindCssVars();

        if (isMobile) {
          tg.viewport.requestFullscreen();
          tg.viewport.expand();
        }

        console.log("TMA viewport initialized");
      });
    }

    if (tg.swipeBehavior.mount.ifAvailable()) {
      tg.swipeBehavior.mount();
      tg.swipeBehavior.disableVertical();
      console.log("TMA swipe behavior initialized");
    }

    if (tg.closingBehavior.mount.ifAvailable()) {
      tg.closingBehavior.mount();
      tg.closingBehavior.enableConfirmation();
      console.log("TMA closing behavior initialized");
    }

    if (import.meta.env.DEV) {
      Object.entries(CSS_VARS_MOCK).forEach(([key, value]) => mockCssVar(key, value));
    }

    console.log(
      import.meta.env.DEV ? "TMA Debug mode in Web initialized" : "Telegram Mini App initialized",
    );

    prefetch(launchParams.tgWebAppData?.user?.photo_url);
  },
  openLink(url: string | URL, options?: tg.OpenLinkOptions) {
    tg.openLink.ifAvailable(url, options);
  },
  hapticFeedback: {
    click() {
      tg.hapticFeedback.impactOccurred.ifAvailable("light");
    },
    inputChange() {
      tg.hapticFeedback.selectionChanged.ifAvailable();
    },
  },
  initData: tg.initData,
  viewport: tg.viewport,
  viewportCss: {
    stableHeight: () =>
      parseFloat(document.documentElement.style.getPropertyValue("--tg-viewport-stable-height")),

    height: () =>
      parseFloat(document.documentElement.style.getPropertyValue("--tg-viewport-height")),
    width: () => parseFloat(document.documentElement.style.getPropertyValue("--tg-viewport-width")),

    safeAreaInsetTop: () =>
      parseFloat(
        document.documentElement.style.getPropertyValue("--tg-viewport-safe-area-inset-top"),
      ),
    contentSafeAreaInsetTop: () =>
      parseFloat(
        document.documentElement.style.getPropertyValue(
          "--tg-viewport-content-safe-area-inset-top",
        ),
      ),

    safeAreaInsetBottom: () =>
      parseFloat(
        document.documentElement.style.getPropertyValue("--tg-viewport-safe-area-inset-bottom"),
      ),
    contentSafeAreaInsetBottom: () =>
      parseFloat(
        document.documentElement.style.getPropertyValue(
          "--tg-viewport-content-safe-area-inset-bottom",
        ),
      ),

    safeAreaInsetLeft: () =>
      parseFloat(
        document.documentElement.style.getPropertyValue("--tg-viewport-safe-area-inset-left"),
      ),
    contentSafeAreaInsetLeft: () =>
      parseFloat(
        document.documentElement.style.getPropertyValue(
          "--tg-viewport-content-safe-area-inset-left",
        ),
      ),

    safeAreaInsetRight: () =>
      parseFloat(
        document.documentElement.style.getPropertyValue("--tg-viewport-safe-area-inset-right"),
      ),
    contentSafeAreaInsetRight: () =>
      parseFloat(
        document.documentElement.style.getPropertyValue(
          "--tg-viewport-content-safe-area-inset-right",
        ),
      ),
  },
  storage: {
    async clear() {
      return fallbackImplementation(
        true,
        [],
        () => tg.cloudStorage.clear.ifAvailable(),
        () => Object.values(STORAGE_KEYS).forEach((key) => localStorage.removeItem(key)),
      );
    },
    async getItem(key: StorageKey) {
      return fallbackImplementation(
        true,
        [key],
        (key) => tg.cloudStorage.getItem.ifAvailable(key),
        (key) => localStorage.getItem(key) ?? "",
      );
    },
    async setItem(key: StorageKey, value: string) {
      return fallbackImplementation(
        true,
        [key, value],
        (key, value) => tg.cloudStorage.setItem.ifAvailable(key, value),
        (key, value) => localStorage.setItem(key, value),
      );
    },
    async deleteItem(key: StorageKey) {
      return fallbackImplementation(
        true,
        [key],
        (key) => tg.cloudStorage.deleteItem.ifAvailable(key),
        (key) => localStorage.removeItem(key as string),
      );
    },
  },
};

export default externalTgApi;

if (import.meta.env.DEV) {
  // @ts-expect-error
  window.TG = externalTgApi;
}

export const useTelegramViewportValue = (key: keyof typeof externalTgApi.viewportCss) => {
  useSignal(externalTgApi.viewport[key]);
  return externalTgApi.viewportCss[key]();
};

export const useTelegramUser = () => useLaunchParams(true).tgWebAppData?.user;
