import { h, render } from 'preact';

import { parseProtocolUrl } from '../common/lbry-url';
import { getSettingsAsync, LbrySettings, redirectDomains } from '../common/settings';
import { YTDescriptor, ytService } from '../common/yt';

interface UpdaterOptions {
  /** invoked if a redirect should be performed */
  onRedirect?(ctx: UpdateContext): void
  /** invoked if a URL is found */
  onURL?(ctx: UpdateContext): void
}

interface UpdateContext {
  descriptor: YTDescriptor
  url: string
  enabled: boolean
  redirect: LbrySettings['redirect']
}

function pauseVideo() { document.querySelectorAll<HTMLVideoElement>('video').forEach(v => v.pause()); }

function openApp(url: string) {
  pauseVideo();
  location.assign(url);
}

async function resolveYT(descriptor: YTDescriptor) {
  const lbryProtocolUrl: string | null = await ytService.resolveById(descriptor).then(a => a[0]);
  const segments = parseProtocolUrl(lbryProtocolUrl || '', { encode: true });
  if (segments.length === 0) return;
  return segments.join('/');
}

/** Compute the URL and determine whether or not a redirect should be performed. Delegates the redirect to callbacks. */
async function handleURLChange(url: URL | Location, { onRedirect, onURL }: UpdaterOptions): Promise<void> {
  const { enabled, redirect } = await getSettingsAsync('enabled', 'redirect');
  const urlPrefix = redirectDomains[redirect].prefix;
  const descriptor = ytService.getId(url.href);
  if (!descriptor) return; // couldn't get the ID, so we're done
  const res = await resolveYT(descriptor);
  if (!res) return; // couldn't find it on lbry, so we're done

  const ctx = { descriptor, url: urlPrefix + res, enabled, redirect };
  if (onURL) onURL(ctx);
  if (enabled && onRedirect) onRedirect(ctx);
}

/** Returns a mount point for the button */
async function findMountPoint(): Promise<HTMLDivElement | void> {
  const sleep = (t: number) => new Promise(resolve => setTimeout(resolve, t));
  let ownerBar = document.querySelector('ytd-video-owner-renderer');
  for (let i = 0; !ownerBar && i < 50; i++) {
    await sleep(200);
    ownerBar = document.querySelector('ytd-video-owner-renderer');
  }

  if (!ownerBar) return;
  const div = document.createElement('div');
  div.style.display = 'flex';
  ownerBar.insertAdjacentElement('afterend', div);
  return div;
}

function WatchOnLbryButton({ url }: { url?: string }) {
  if (!url) return null;
  return <div style={{ display: 'flex', justifyContent: 'center', flexDirection: 'column' }}>
    <a href={url} onClick={pauseVideo} role='button'
      children={<div>
        <img src={chrome.runtime.getURL('icons/lbry-logo.svg')} height={10} width={14}
          style={{ marginRight: 12, transform: 'scale(1.75)' }} />
        Watch on LBRY
        </div>}
      style={{
        borderRadius: '2px',
        backgroundColor: '#075656',
        border: '0',
        color: 'whitesmoke',
        padding: '10px 16px',
        marginRight: '5px',
        fontSize: '14px',
        textDecoration: 'none',
      }} />
  </div>
}


const mountPointPromise = findMountPoint();

const handle = (url: URL | Location) => handleURLChange(url, {
  async onURL({ descriptor: { type }, url }) {
    const mountPoint = await mountPointPromise;
    if (type !== 'video' || !mountPoint) return;
    render(<WatchOnLbryButton url={url} />, mountPoint)
  },
  onRedirect({ redirect, url }) {
    if (redirect === 'app') return openApp(url);
    location.replace(url);
  },
});

// handle the location on load of the page
handle(location);

/*
 * Gets messages from background script which relays tab update events. This is because there's no sensible way to detect
 * history.pushState changes from a content script
 */
chrome.runtime.onMessage.addListener(async (req: { url: string }) => {
  mountPointPromise.then(mountPoint => mountPoint && render(<WatchOnLbryButton />, mountPoint))
  if (!req.url) return;
  handle(new URL(req.url));
});

chrome.storage.onChanged.addListener((changes, areaName) => {
  if (areaName !== 'local' || !changes.redirect) return;
  handle(new URL(location.href))
});
