import React, { useState } from 'react';

import { generateArticle } from '../../service/visibility/visibility.service';

import ArticleModal from './ArticleModal';
import ArticleProgress from './ArticleProgress';
import GenerateLanguagesDialog from './GenerateLanguagesDialog';

interface TopWinsLossesProps {
  topWins: string[];
  topLosses: string[];
  brandId: string;
  clientId: string;
  token: string;
  language?: string | null;
  /**
   * Bubbled up when a loss row's generated article finishes. Lets the
   * parent dashboard refetch the Articles list / tab counts so the new
   * article appears in the Recommended section without a manual page
   * refresh.
   */
  onArticleGenerated?: () => void;
}

const MAX_ROWS = 5;

/**
 * Two side-by-side cards:
 *   - "Where you appeared this week" (green) - ``top_wins``
 *   - "Where you missed out" (orange) - ``top_losses`` + "Generate
 *     article" button that dispatches article generation with the loss
 *     query pre-filled as the topic.
 */
const TopWinsLosses = ({
  topWins,
  topLosses,
  brandId,
  clientId,
  token,
  language,
  onArticleGenerated,
}: TopWinsLossesProps): JSX.Element | null => {
  // Loss row ({index, topic}) awaiting the "which languages?" dialog before
  // its article is dispatched. Null when the dialog is closed.
  const [langDialogLoss, setLangDialogLoss] = useState<{
    index: number;
    topic: string;
  } | null>(null);
  const [pendingByLoss, setPendingByLoss] = useState<Record<number, string>>(
    {}
  );
  const [errorByLoss, setErrorByLoss] = useState<Record<number, string>>({});
  const [busyByLoss, setBusyByLoss] = useState<Record<number, boolean>>({});
  // Loss indices whose generated article landed ``ready`` in this
  // session - hidden from the rendered list so the merchant stops
  // seeing a "Generate article" CTA over a topic they just covered.
  const [dismissedLosses, setDismissedLosses] = useState<Set<number>>(
    () => new Set()
  );
  const [openArticleId, setOpenArticleId] = useState<string | null>(null);

  if (topWins.length === 0 && topLosses.length === 0) return null;

  const handleGenerate = async (
    lossIndex: number,
    topic: string,
    languages: string[]
  ) => {
    setLangDialogLoss(null);
    setBusyByLoss(prev => ({ ...prev, [lossIndex]: true }));
    setErrorByLoss(prev => {
      const next = { ...prev };
      delete next[lossIndex];

      return next;
    });
    try {
      const dispatch = await generateArticle(clientId, token, {
        brand_id: brandId,
        topic,
        // Anchor the article to the originating loss so the backend can
        // drop this row from "Where you missed out" on subsequent
        // weekly-report fetches.
        target_prompts: [topic],
        language: language ?? undefined,
        languages: languages.length ? languages : undefined,
        triggered_by: 'manual',
      });
      setPendingByLoss(prev => ({
        ...prev,
        [lossIndex]: dispatch.article_id,
      }));
    } catch (err) {
      setErrorByLoss(prev => ({
        ...prev,
        [lossIndex]:
          err instanceof Error ? err.message : 'Failed to start generation.',
      }));
    } finally {
      setBusyByLoss(prev => ({ ...prev, [lossIndex]: false }));
    }
  };

  const visibleWins = topWins.slice(0, MAX_ROWS);
  const visibleLosses = topLosses
    .slice(0, MAX_ROWS)
    .map((loss, index) => ({ loss, index }))
    .filter(({ index }) => !dismissedLosses.has(index));

  return (
    <>
      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
        {/* Wins */}
        <div className="bg-white rounded-xl border border-gray-200 p-4">
          <div className="flex flex-col gap-3">
            <h3 className="text-base font-semibold text-gray-900">
              Where you appeared this week
            </h3>
            {visibleWins.length === 0 ? (
              <p className="text-xs text-gray-500">
                No wins recorded for this week.
              </p>
            ) : (
              <ul className="m-0 p-0 list-none">
                {visibleWins.map((win, idx) => (
                  <li
                    key={idx}
                    className="flex items-start gap-2.5 py-2"
                    style={{
                      borderBottom:
                        idx < visibleWins.length - 1
                          ? '1px solid #e1e3e5'
                          : 'none',
                    }}
                  >
                    <span
                      aria-hidden="true"
                      className="flex-shrink-0 w-4.5 h-4.5 rounded-full bg-green-100 text-green-800 inline-flex items-center justify-center font-bold text-xs"
                      style={{ width: 18, height: 18 }}
                    >
                      ✓
                    </span>
                    <span className="text-sm text-gray-900">{win}</span>
                  </li>
                ))}
              </ul>
            )}
          </div>
        </div>

        {/* Losses */}
        <div className="bg-white rounded-xl border border-gray-200 p-4">
          <div className="flex flex-col gap-3">
            <h3 className="text-base font-semibold text-gray-900">
              Where you missed out
            </h3>
            {visibleLosses.length === 0 ? (
              <p className="text-xs text-gray-500">
                No missed opportunities flagged this week.
              </p>
            ) : (
              <ul className="m-0 p-0 list-none">
                {visibleLosses.map(({ loss, index: idx }, position) => {
                  const pendingId = pendingByLoss[idx];
                  const isGenerating = Boolean(pendingId);
                  const err = errorByLoss[idx];

                  return (
                    <li
                      key={idx}
                      className="flex flex-col gap-1.5 py-2.5"
                      style={{
                        borderBottom:
                          position < visibleLosses.length - 1
                            ? '1px solid #e1e3e5'
                            : 'none',
                      }}
                    >
                      <div className="flex items-center justify-between gap-2 flex-wrap">
                        <div className="flex items-start gap-2.5 flex-1 min-w-0">
                          <span
                            aria-hidden="true"
                            className="flex-shrink-0 rounded-full bg-orange-100 text-orange-800 inline-flex items-center justify-center font-bold text-xs"
                            style={{ width: 18, height: 18 }}
                          >
                            !
                          </span>
                          <span className="text-sm text-gray-900">{loss}</span>
                        </div>
                        <button
                          type="button"
                          onClick={() =>
                            setLangDialogLoss({ index: idx, topic: loss })
                          }
                          disabled={isGenerating || busyByLoss[idx]}
                          className="inline-flex items-center rounded-md border border-gray-300 bg-white px-2.5 py-1 text-xs font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
                        >
                          {busyByLoss[idx]
                            ? 'Starting...'
                            : isGenerating
                              ? 'Generating…'
                              : 'Generate content'}
                        </button>
                      </div>

                      {err && <p className="text-xs text-red-700">{err}</p>}

                      {isGenerating && pendingId && (
                        <ArticleProgress
                          articleId={pendingId}
                          clientId={clientId}
                          token={token}
                          onReady={article => {
                            setPendingByLoss(prev => {
                              const next = { ...prev };
                              delete next[idx];

                              return next;
                            });
                            setDismissedLosses(prev => {
                              const next = new Set(prev);
                              next.add(idx);

                              return next;
                            });
                            setOpenArticleId(article.summary.article_id);
                            onArticleGenerated?.();
                          }}
                          onFailed={message =>
                            setErrorByLoss(prev => ({
                              ...prev,
                              [idx]: message,
                            }))
                          }
                        />
                      )}
                    </li>
                  );
                })}
              </ul>
            )}
          </div>
        </div>
      </div>

      <ArticleModal
        open={openArticleId !== null}
        articleId={openArticleId}
        onClose={() => setOpenArticleId(null)}
        clientId={clientId}
        token={token}
      />

      <GenerateLanguagesDialog
        open={langDialogLoss !== null}
        onOpenChange={next => {
          if (!next) setLangDialogLoss(null);
        }}
        primaryLanguage={language || 'en'}
        busy={
          langDialogLoss ? Boolean(busyByLoss[langDialogLoss.index]) : false
        }
        onConfirm={languages => {
          if (langDialogLoss) {
            void handleGenerate(
              langDialogLoss.index,
              langDialogLoss.topic,
              languages
            );
          }
        }}
      />
    </>
  );
};

export default TopWinsLosses;
