import React, { useState, useEffect } from 'react';
import { AppStateProvider, useDraft, useDraftUsage, useAppStateDispatch } from './Context.js';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import Keywords from './pages/Keywords.js';
import Competitors from './pages/Competitors.js';
import Guidance from './pages/Guidance.js';
import ContentOutline from './pages/ContentOutline.js';
import Footer from './components/Footer.js';
import DraftUsageAlert from './components/DraftUsageAlert.js';
import './App.css';
import ErrorBoundary from './utilities/ErrorBoundary.js';

function AppInner() {
  const [tabIndex, setTabIndex] = useState(0);
  const { maxDrafts, usedDrafts, alertIsDismissed } = useDraftUsage();
  const dispatch = useAppStateDispatch();
  const draft = useDraft();

  // Calculate if user has run out of drafts
  const hasRunOutOfDrafts = maxDrafts - usedDrafts <= 0;
  // Show tabs if: user has drafts available OR has an existing draft ID
  const shouldShowTabs = !hasRunOutOfDrafts || draft?.id;
  // Show alert if the quota spent is higher than 2/3.
  const shouldShowDraftUsageAlert = usedDrafts / maxDrafts > 0.6 && !alertIsDismissed;

  return (
    <div className="conductor">
      {shouldShowDraftUsageAlert && (
        <DraftUsageAlert
          usedDrafts={usedDrafts}
          maxDrafts={maxDrafts}
          onClick={() =>
            dispatch({
              type: 'setDraftUsageAlertIsDismissed',
              draftUsageAlertIsDismissed: true,
            })
          }
        />
      )}
      {shouldShowTabs ? (
        <Tabs
          className="conductor__tabs"
          selectedIndex={tabIndex}
          onSelect={(index) => setTabIndex(index)}
        >
          <TabList className="conductor__tab-list">
            <Tab className="conductor__tab">Keywords</Tab>
            <Tab className="conductor__tab">Competitors</Tab>
            <Tab className="conductor__tab">Guidance</Tab>
            <Tab className="conductor__tab">Content outline</Tab>
          </TabList>
          <TabPanel className="conductor__tab-panel">
            <Keywords setTab={(index: number) => setTabIndex(index)} nextTab={1} />
          </TabPanel>
          <TabPanel className="conductor__tab-panel">
            <Competitors setTab={(index: number) => setTabIndex(index)} nextTab={2} />
          </TabPanel>
          <TabPanel className="conductor__tab-panel">
            <Guidance setTab={(index: number) => setTabIndex(index)} nextTab={3} />
          </TabPanel>
          <TabPanel className="conductor__tab-panel">
            <ContentOutline />
          </TabPanel>
          <Footer />
        </Tabs>
      ) : (
        <div className="conductor__no-drafts-message">
          You have reached your draft limit. Please contact support to increase your limit.
        </div>
      )}
    </div>
  );
}

type Settings = {
  accountId: string;
  maxDrafts: number;
  usedDrafts: number;
};

// history.pushState() doesnt fire a popstate event, so dispatch a custom event.
const originalPushState = window.history.pushState.bind(window.history);
window.history.pushState = (...args: Parameters<typeof History.prototype.pushState>) => {
  originalPushState(...args);
  window.dispatchEvent(new Event('locationchange'));
};

function getEntityFromUrl(): { entityType: string; entityId: number } {
  const parts = window.location.pathname.split('/').filter(Boolean);
  // The parameter is `/canvas/editor/{entity_type}/{entity_id}`
  if (parts[0] === 'canvas' && parts[1] === 'editor') {
    const entityType = parts[2] ?? '';
    const entityId = parts[3] ? parseInt(parts[3]) : 0;
    return { entityType, entityId };
  }
  return { entityType: '', entityId: 0 };
}

function App() {
  const [appKey, setAppKey] = useState(0);
  const [settings, setSettings] = useState<Settings | null>(null);
  const [settingsError, setSettingsError] = useState(false);
  const [draftId, setDraftId] = useState('');
  const [url, setUrl] = useState(window.location.href);
  const [loadedUrl, setLoadedUrl] = useState<string | null>(null);

  useEffect(() => {
    const handleUrlChange = () => setUrl(window.location.href);
    window.addEventListener('popstate', handleUrlChange);
    window.addEventListener('locationchange', handleUrlChange);
    return () => {
      window.removeEventListener('popstate', handleUrlChange);
      window.removeEventListener('locationchange', handleUrlChange);
    };
  }, []);

  useEffect(() => {
    setSettingsError(false);
    const { entityType, entityId } = getEntityFromUrl();
    let cancelled = false;

    const settingsFetch = fetch('/conductor/api/settings').then((res) => {
      if (!res.ok) throw new Error();
      return res.json() as Promise<Settings>;
    });

    const draftFetch =
      entityType && entityId
        ? fetch(`/conductor/api/draft/${entityType}/${entityId}`)
            .then((res) => {
              if (res.status === 404) return null;
              if (!res.ok) throw new Error();
              return res.json();
            })
            .catch(() => null)
        : Promise.resolve(null);

    Promise.all([settingsFetch, draftFetch])
      .then(([settingsData, draftData]) => {
        if (!cancelled) {
          setSettings(settingsData);
          setDraftId(draftData?.draft_id ?? '');
          setLoadedUrl(url);
        }
      })
      .catch(() => {
        if (!cancelled) {
          setSettingsError(true);
          setLoadedUrl(url);
        }
      });

    return () => {
      cancelled = true;
    };
  }, [url]);

  const { entityType, entityId } = getEntityFromUrl();

  // Return null until both fetches for the current URL have resolved.
  // This prevents a stale-data flash on the first render after navigation,
  // before the effects have had a chance to reset state.
  if (loadedUrl !== url) {
    return null;
  }

  if (settingsError) {
    return 'Unable to fetch settings, please reload the app.';
  }

  const { accountId, maxDrafts, usedDrafts } = settings!;

  if (!entityType || !entityId) {
    return 'Please open a Canvas page before using this extension.';
  }

  return (
    <ErrorBoundary onReset={() => setAppKey((k) => k + 1)}>
      <AppStateProvider
        key={appKey}
        initialState={{
          accountId,
          draft: { id: draftId },
          drupal: { entity: { type: entityType, id: entityId }, draftLinked: draftId.length > 0 },
          draftUsage: { maxDrafts, usedDrafts, alertIsDismissed: false },
        }}
      >
        <AppInner />
      </AppStateProvider>
    </ErrorBoundary>
  );
}

export default App;
