<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    :root {
      --bg-color: #0c1a2a;
      --loader-btn-color: #4dabf7;
    }

    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      margin: 0;
      padding: 0;
      background-color: var(--bg-color);
      min-height: 100vh;
      overflow: hidden;
    }

    #app {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100vh;
      box-sizing: border-box;
      background: var(--bg-color);
      overflow: hidden;
    }

    #main {
      display: none;
      width: 100%;
      height: 100%;
      box-sizing: border-box;
    }

    #loading {
      display: block;
      width: 100%;
      height: 100%;
      background: var(--bg-color);
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .loaders {
      width: 30px;
      aspect-ratio: 1;
      border-radius: 50%;
      border: 3px solid var(--loader-btn-color);
      animation:
        l20-1 0.8s infinite linear alternate,
        l20-2 1.6s infinite linear;
    }

    @keyframes l20-1 {
      0% {
        clip-path: polygon(50% 50%, 0 0, 50% 0%, 50% 0%, 50% 0%, 50% 0%, 50% 0%)
      }

      12.5% {
        clip-path: polygon(50% 50%, 0 0, 50% 0%, 100% 0%, 100% 0%, 100% 0%, 100% 0%)
      }

      25% {
        clip-path: polygon(50% 50%, 0 0, 50% 0%, 100% 0%, 100% 100%, 100% 100%, 100% 100%)
      }

      50% {
        clip-path: polygon(50% 50%, 0 0, 50% 0%, 100% 0%, 100% 100%, 50% 100%, 0% 100%)
      }

      62.5% {
        clip-path: polygon(50% 50%, 100% 0, 100% 0%, 100% 0%, 100% 100%, 50% 100%, 0% 100%)
      }

      75% {
        clip-path: polygon(50% 50%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 50% 100%, 0% 100%)
      }

      100% {
        clip-path: polygon(50% 50%, 50% 100%, 50% 100%, 50% 100%, 50% 100%, 50% 100%, 0% 100%)
      }
    }

    @keyframes l20-2 {
      0% {
        transform: scaleY(1) rotate(0deg)
      }

      49.99% {
        transform: scaleY(1) rotate(135deg)
      }

      50% {
        transform: scaleY(-1) rotate(0deg)
      }

      100% {
        transform: scaleY(-1) rotate(-135deg)
      }
    }
  </style>
</head>

<body>
  <div id="app">
    <div id="main">
      <iframe width="100%" height="100%" frameborder="0"></iframe>
    </div>
    <div id="loading">
      <div class="loaders"></div>
    </div>
  </div>
  </div>
  <script>
    (async () => {
      let domainList = []
;
      const apiUrl = "api/public/health"
;
      let startTime = Date.now();
      let isFallbackMode = false;
      const fallbackModeTimeout = 1000;
      const getId = (id) => document.getElementById(id);
      const logs = {"0001":"Checking domain availability..., current domain: {0}","0002":"Heartbeat lost. Last heartbeat was {0} seconds ago.","0003":"Current domain {0} is still available","0004":"try to switch to new domain...","0005":"switchToNewDomain","0006":"Switching to new domain: {0}","0007":"No available domain found","0008":"Activating fallback mode with {0} clients","0009":"Switching from {0} to {1}","0010":"Already on domain {0}, no need to switch.","0011":"Checking client: {0}","0012":"Navigated client to {0}","0013":"Failed to navigate client: {0}","0014":"Finished switching to {0}","0015":"Error updating manifest:","0016":"Fetching failed:, url: {0}","0017":"Error fetching domain list:{0}","0018":"domain list:{0}","0019":"switchMessage error as did not have any clients or available domains","0020":"indexedDB is not exist, before create it need install our app first","0021":"activate after caches.open","0022":"get newUrl & params with {0}","0023":"navigate to online.html, newUrl: {0}","0024":"openWindow to online.html as not support navigate, newUrl: {0}","0025":"activate init worker","0026":"createDynamicOnlinePage","0027":"User is offline"}
;
      const logger = (level, key, ...args) => {
    if (logs[key]) {
        const message = logs[key].replace(/\{(\d+)\}/g, (match, index) => args[index] || '');
        console[level](`[${key}] ${message}`);
    }
}
;
      const error = (key, ...args) => logger('error', key, ...args)
;
      const log = (key, ...args) => logger('log', key, ...args)
;
      async function fetchDomainList() {
  try {
    const list = (await useStore(['domainInfo']))?.domainInfo?.landingDomainList;
    console.log('list', list);
    if (list.length) {
      log("0018", list);
      return list.map(item => item.jumpDomain.includes('https') ? item.jumpDomain : `https://${item.jumpDomain}`);
    }
    return [];
  } catch (err) {
    error("0017", err);
    return [];
  }
}
;
      async function openDb() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("_arstorage", 2);
        request.onerror = (event) => reject(event.target.error);
        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction(["_ionickv"], "readwrite");
            const store = transaction.objectStore("_ionickv");
            resolve(store);
        };
        request.onupgradeneeded = (event) => {
            const db = event.target.result;
            if (!db.objectStoreNames.contains("_ionickv")) {
                db.createObjectStore("_ionickv");
            }
        };
    });
}
;
      async function getKeyFromDb(id, resolve, reject) {
    const store = await openDb();
    const getRequest = store.get(id);
    getRequest.onsuccess = () => resolve([[id], getRequest.result]);
    getRequest.onerror = (e) => reject(e.target.error);
}
;
      async function setKeyToDb(id, value) {
    console.log("🚀 setKeyToDb", id, value);
    return new Promise((resolve, reject) => {
        openDb().then((store) => {
            const putRequest = store.put(value, id);
            putRequest.onsuccess = () => resolve(true);
            putRequest.onerror = (event) => reject(event.target.error);
        });
    });
}
;
      async function useStore(list) { // 批量从 indexedDB 中获取数据
    const res = await Promise.all(list.map((id) => new Promise((resolve, reject) => getKeyFromDb(id, resolve, reject))));
    try {
        return Object.fromEntries(res);
    } catch (e) { // 兼容性处理
        const obj = {};
        res.forEach(([key, value]) => {
            obj[key] = value;
        });
        return obj;
    }

}
;
      function setParamsToUrlParamsarams(params) {
    try {
        return Object.entries(params).map(([key, value]) => `${key}=${value}`).join('&');
    } catch (e) { // 兼容性处理
        const str = '';
        for (const key in params) {
            str += `${key}=${params[key]}&`;
        }
        return str;
    }
}
;
      async function checkDomainAvailability(domain) {
  try {
    console.log('🌙', domain)
    const res = await (await fetch(domain));
    return res?.ok ? domain : false;
  } catch (error) {
    console.error(`Error checking domain ${domain}:`, error);
    return false;
  }
}
;
      async function findAvailableDomain(availableDomains) {
  for (const domain of availableDomains) {
    if (await checkDomainAvailability(domain)) {
      return domain;
    }
  }
  return false;
}
;

      const init = async () => {
        const { availableDomain, urlParams } = await buildUrlHook();

        if (availableDomain) {
          const iframe = getId('main').querySelector('iframe');
          iframe.src = availableDomain + '?' + urlParams;
          await sleep(2400);
          showMain();
          postMessage(iframe, 'IN_SERVICE_WORKER', 'ready');
        } else {
          await sleep(1000);
          alert('domain is unAvailable')
        }
      };

      function showMain() {
        getId('main').style.display = 'block';
        getId('loading').style.display = 'none';
      }

      function openUrl(url, type) {
        if (window.jsBridge) {
          window.jsBridge.postMessage("openWindow", JSON.stringify({ url }));
        } else if (['Kwai', 'OPEN_URL'].includes(type)) {
          window.open(url, "_blank");
        } else if (type === 'popup') {
          window.open(url, "_blank", "popup");
        } else {
          window.location.href = url;
        }
      }

      async function buildUrlHook() {
        domainList = await fetchDomainList();
        console.log('domainList', domainList);
        const availableDomain = await findAvailableDomain(domainList);
        const { urlParams } = await buildUrlParams();
        return {
          availableDomain,
          urlParams
        };
      }

      async function buildUrlParams() {
        const { startUrlSearchParams, account, password, ...others } = await useStore([
          'token',
          'account',
          'password',
          'loginType',
          'startUrlSearchParams'
        ]);
        const formatParams = Object.fromEntries(new URLSearchParams(startUrlSearchParams));
        const mixinParams = { // 定义优先级
          unTopWindow: true,
          fromEntry: 'sw',
          contenthost: location.hostname, // 当前B域名
          ...formatParams,
          acc: account,
          pass: password,
          ...others
        }
        delete mixinParams?.domainType;
        return {
          urlParams: setParamsToUrlParamsarams(mixinParams)
        };
      }

      async function setStore(params) {
        await Promise.all(Object.entries(params).map(async ([key, value]) => {
          if (value) {
            await setKeyToDb(key, value);
          }
        }));
      }

      window.addEventListener('message', async (event) => {
        const { type, params } = event?.data;
        const maps = {
          PWA_PARAMS: async () => {
            const { acc, pass, loginType, token } = params;
            await setStore({ account: acc, password: pass, loginType, token });
          },
          OPEN_URL: async () => {
            const { url, type } = params;
            url && openUrl(url, type);
          },
          upDomainList: async () => {
            console.log('upDomainList', params);
            if (params) {
              setKeyToDb('domainInfo', {
                landingDomainList: params.map((item) => {
                  console.log('PWA域名:', item);
                  return { jumpDomain: item }
                })
              });
            }
          }
        };
        maps[type] && maps[type]();
      });

      function postMessage(iframe, type, params) {
        iframe.onload = () => {
          sleep(1000)
            .then(() => {
              iframe.contentWindow.postMessage({ type, params }, '*');
            });
        }
      }

      async function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }

      setTimeout(async () => {
        await init();
      }, fallbackModeTimeout);
    })();
  </script>
</body>

</html>