import React, { useState, useEffect } from 'react';
import {
  useKeywords,
  useRankSource,
  useAppStateDispatch,
  useAccountId,
  useKeywordsEvaluation,
} from '../Context.js';
import useSWRImmutable from 'swr/immutable';
import { get, post } from '../utilities/fetcher.js';
import DraftFetcher from '../utilities/DraftFetcher.js';
import Button from '../components/Button.js';
import ErrorMessage from '../components/ErrorMessage.js';
import Loading from '../components/Loading.js';
import RankSourceSelector from '../components/RankSourceSelector.js';
import Tag from '../components/Tag.js';
import TagInput from '../components/TagInput.js';
import './Keywords.css';

interface PhraseDetail {
  rootKeyword: string;
  topicSearchVolume: number;
  topicPhrase: string;
}

const Keywords = ({
  setTab = (_: number) => {},
  nextTab = 0,
}: {
  setTab?: (index: number) => void;
  nextTab?: number;
}) => {
  const dispatch = useAppStateDispatch();
  const keywords = useKeywords();
  const rankSource = useRankSource();
  const accountId = useAccountId();
  const evaluate = useKeywordsEvaluation();

  const [draftLoading, setDraftLoading] = useState(true);
  const [draftError, setDraftError] = useState(false);

  // Error and loading states depending on multiple endpoints so we can't rely on the results from
  // SWR, otherwise it will flicker in and out.
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);

  const maxKeywords = 5;

  const {
    data: requestInsights,
    isLoading: isLoadingRequestInsights,
    error: errorRequestInsights,
  } = useSWRImmutable(
    evaluate ? [`/conductor/proxy/v3/${accountId}/content-guidance`, JSON.parse(evaluate)] : null,
    post,
  );

  const {
    data: insights,
    isLoading: isLoadingInsights,
    error: errorInsights,
  } = useSWRImmutable(
    requestInsights?.request_id
      ? `/conductor/proxy/v3/${accountId}/content-guidance?${new URLSearchParams({
          requestUuid: requestInsights.request_id,
        })}`
      : null,
    async (url) => {
      const response = await get(url);

      // Check if all statuses are COMPLETED.
      const isCompleted = (status: string) =>
        status === 'COMPLETED' || status === 'COMPLETED_WITH_ERROR';

      const allCompleted =
        isCompleted(response.status) &&
        isCompleted(response.insightsStatus) &&
        isCompleted(response.snippetsStatus);

      if (!allCompleted) {
        // Throw an error to trigger SWR's retry mechanism.
        throw new Error('Processing not complete');
      }
      return response;
    },
    {
      refreshInterval: (data) => (data ? 0 : 250), // Stop refreshing once data is available.
      errorRetryCount: 50,
      errorRetryInterval: 250,
    },
  );

  useEffect(() => {
    if (!isLoadingRequestInsights && !isLoadingInsights && insights) {
      setLoading(false);
    }

    const completedWithError = (status: string | null) => status === 'COMPLETED_WITH_ERROR';
    if (
      (errorRequestInsights && !isLoadingRequestInsights) ||
      (errorInsights &&
        errorInsights?.message !== 'Processing not complete' &&
        !isLoadingInsights) ||
      completedWithError(insights?.status) ||
      completedWithError(insights?.insightsStatus || completedWithError(insights?.snippetsStatus))
    ) {
      setError(true);
    }
  }, [insights, isLoadingRequestInsights, isLoadingInsights, errorRequestInsights, errorInsights]);

  const keywordInsights =
    insights?.results?.snippets
      ?.filter((snippet: { type: string }) => snippet.type === 'MSV_SNIPPET')
      .pop()
      .phraseDetails?.sort((a: { rootKeyword: string }, b: { rootKeyword: string }) => {
        return keywords.indexOf(a.rootKeyword) - keywords.indexOf(b.rootKeyword);
      }) || [];

  const potentialClicksPerMonth =
    insights?.results?.snippets
      ?.filter((snippet: { type: string }) => snippet.type === 'OPPORTUNITIES_SNIPPET')
      .pop()
      ?.details?.topUrls?.filter((topUrl: { rank: number; traffic: number }) => topUrl.rank === 1)
      .pop() || null;

  const handleEvaluate = (): void => {
    if (keywords.length > 0) {
      setLoading(true);
      setError(false);
      dispatch({
        type: 'setKeywordsEvaluation',
        keywordsEvaluation: JSON.stringify({
          accountId,
          input: {
            topic: {
              phrases: keywords,
              phrase: keywords[0],
              isoLocale: rankSource.isoLocale,
              rankSourceId: rankSource.id,
              webPropertyId: 0,
            },
          },
          request: {
            insights: ['MSV_SNIPPET', 'OPPORTUNITIES_SNIPPET'],
          },
        }),
      });
    }
  };

  const renderKeywords = (
    <>
      <p className="keywords__description">
        Choose up to {maxKeywords} keywords to write about. These will determine what kinds of
        guidance we provide about your content.
      </p>

      <div className="keywords__input">
        <div className="keywords__input-header">
          <label className="keywords__input-label">Keywords</label>
          <span className="keywords__counter">
            {keywords.length} / {maxKeywords}
          </span>
        </div>

        <TagInput
          maxKeywords={maxKeywords}
          placeholder="Keywords you want to write about"
          tags={keywords}
          setTags={(tags) => {
            dispatch({ type: 'setKeywords', keywords: tags });
          }}
          aria-label="Enter keywords"
        />
      </div>
      <div className="keywords__rank-evaluate">
        <RankSourceSelector
          accountId={accountId}
          rankSource={rankSource}
          setRankSource={(rankSource) => {
            dispatch({ type: 'setRankSource', rankSource });
          }}
        />
        <Button
          onClick={handleEvaluate}
          variant="surface"
          size="1"
          disabled={
            keywords.length === 0 ||
            (JSON.stringify(keywords) ===
              JSON.stringify(
                keywordInsights.map((keywordInsight: PhraseDetail) => keywordInsight.topicPhrase),
              ) &&
              insights?.request?.insight?.input?.topic?.rankSourceId === rankSource.id)
          }
        >
          Evaluate
        </Button>
      </div>
      {error && (
        <div className="keywords__errror">
          <ErrorMessage>
            Something went wrong. <button onClick={handleEvaluate}>Please try again.</button>
          </ErrorMessage>
        </div>
      )}
      {loading && !error && !keywordInsights.length && (
        <div className="keywords__loading">
          <Loading />
        </div>
      )}
      {keywordInsights.length > 0 && (
        <div className="keywords__evaluate-results">
          <table>
            <thead>
              <tr>
                <th>Total monthly search volume</th>
                <th>
                  {keywordInsights.reduce((acc: number, val: PhraseDetail) => {
                    acc = acc + val.topicSearchVolume;
                    return acc;
                  }, 0)}
                </th>
              </tr>
            </thead>
            <tbody>
              {keywordInsights.map((keywordInsight: PhraseDetail) => (
                <tr key={keywordInsight.topicPhrase}>
                  <td>
                    <Tag>{keywordInsight.topicPhrase}</Tag>
                  </td>
                  <td>{keywordInsight.topicSearchVolume.toLocaleString()}</td>
                </tr>
              ))}
            </tbody>
            {potentialClicksPerMonth?.rank && potentialClicksPerMonth?.traffic && (
              <tfoot>
                <tr>
                  <td>Rank {potentialClicksPerMonth.rank} potential clicks per month</td>
                  <td>{potentialClicksPerMonth.traffic}</td>
                </tr>
              </tfoot>
            )}
          </table>
        </div>
      )}
      <div className="keywords__generate-guidance">
        <Button disabled={!keywords.length} onClick={() => setTab(nextTab)}>
          Generate content guidance
        </Button>
      </div>
    </>
  );

  return (
    <div
      className={`keywords ${draftLoading ? `keywords--loading` : ''} ${draftError ? `keywords--error` : ''}`}
    >
      <DraftFetcher
        isLoading={(isLoading) => setDraftLoading(isLoading)}
        hasError={(draftError) => setDraftError(draftError)}
      />
      {draftError && (
        <ErrorMessage>
          Unable to load your draft from Conductor. Please try again later.
        </ErrorMessage>
      )}
      {draftLoading && !draftError && <Loading />}
      {!draftLoading && !draftError && renderKeywords}
    </div>
  );
};

export default Keywords;
