import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { HiSparkles as Sparkles } from 'react-icons/hi';
import {
  FaPlusCircle as PlusCircle,
  FaRegEnvelope as Mail,
  FaRegQuestionCircle as HelpCircle,
} from 'react-icons/fa';

import { useAppStateContext } from '../context/user.data.context';
import { useCopilotDataChanged } from '../hooks/useCopilotDataChanged';
import {
  dispatchContentCheck,
  getContentCheckJob,
  getLatestContentCheck,
  listContentCheckProducts,
  rerunContentCheck,
} from '../service/catalog-quality/catalog-quality.service';
import { getActiveContentGenJob } from '../service/content-generator/content-generator.service';
import BulkImproveModal from '../components/catalog-quality/BulkImproveModal';
import ProductActionModal from '../components/catalog-quality/ProductActionModal';
import type {
  ContentCheckSeverity,
  IContentCheckJob,
  IContentCheckProductRow,
} from '../service/catalog-quality/catalog-quality.interface';

const SEVERITY_LABELS: Record<ContentCheckSeverity, string> = {
  critical: 'Critical',
  warning: 'Warnings',
  ok: 'OK',
};

const SEVERITY_BADGE: Record<
  ContentCheckSeverity,
  { label: string; badge: string }
> = {
  critical: { label: 'Critical', badge: 'bg-red-50 text-red-600' },
  warning: { label: 'Warning', badge: 'bg-amber-50 text-amber-700' },
  ok: { label: 'OK', badge: 'bg-emerald-50 text-emerald-700' },
};

const TAB_STYLES: Record<
  'all' | ContentCheckSeverity,
  { active: string; inactive: string }
> = {
  all: {
    active: 'bg-gray-100 text-gray-900',
    inactive: 'text-gray-500 hover:bg-gray-50 hover:text-gray-900',
  },
  critical: {
    active: 'bg-red-50 text-red-700',
    inactive: 'text-red-600 hover:bg-red-50 hover:text-red-700',
  },
  warning: {
    active: 'bg-amber-50 text-amber-700',
    inactive: 'text-amber-600 hover:bg-amber-50 hover:text-amber-700',
  },
  ok: {
    active: 'bg-emerald-50 text-emerald-700',
    inactive: 'text-emerald-600 hover:bg-emerald-50 hover:text-emerald-700',
  },
};

const CatalogQuality = (): JSX.Element => {
  const { token, clientId } = useAppStateContext();
  const [searchParams, setSearchParams] = useSearchParams();

  const jobIdParam: string | null = searchParams.get('job');
  const severityParam: ContentCheckSeverity | null = (() => {
    const raw = searchParams.get('severity');
    if (raw === 'critical' || raw === 'warning' || raw === 'ok') return raw;

    return null;
  })();

  const [job, setJob] = useState<IContentCheckJob | null>(null);
  const [products, setProducts] = useState<IContentCheckProductRow[]>([]);
  const [dispatching, setDispatching] = useState<boolean>(false);
  const [rerunBusy, setRerunBusy] = useState<boolean>(false);
  // True while a content-gen job (e.g. a bulk improve) runs. The backend
  // serialises these against the audit, so every catalog action is disabled
  // while one is in flight.
  const [bulkRunning, setBulkRunning] = useState<boolean>(false);
  // bulkScope === null → closed; {} → full catalog; {sku, productName} → one row.
  const [bulkScope, setBulkScope] = useState<{
    sku?: string;
    productName?: string;
  } | null>(null);

  const refreshJob = useCallback(async (): Promise<void> => {
    if (!token) return;
    const fresh: IContentCheckJob | null = jobIdParam
      ? await getContentCheckJob(jobIdParam).catch(() => null)
      : await getLatestContentCheck().catch(() => null);
    setJob(fresh);
    if (fresh) {
      const page = await listContentCheckProducts(fresh.job_id, {
        limit: 50,
        severity: severityParam,
      }).catch(() => ({ products: [], next_cursor: null }));
      setProducts(page.products);
    } else {
      setProducts([]);
    }
  }, [token, jobIdParam, severityParam]);

  useEffect(() => {
    refreshJob();
  }, [refreshJob]);

  // When the copilot runs a content check (or a bulk improve) it dispatches
  // ``copilot:data-changed``; re-pull the latest job + per-product results so
  // the audit score and rows reflect the new run without a manual reload. The
  // in-flight poll below then continues advancing a still-running job.
  useCopilotDataChanged(() => {
    void refreshJob();
  });

  // Lightweight poll for an in-flight content-gen job (e.g. a bulk improve).
  // Runs continuously while the page is open - decoupled from the audit poll
  // - so a bulk started from this page disables the buttons within one tick,
  // and re-enables them once it finishes.
  const refreshBulkRunning = useCallback(async (): Promise<void> => {
    if (!clientId) return;
    const active = await getActiveContentGenJob(clientId).catch(() => null);
    setBulkRunning(!!active?.data?.active);
  }, [clientId]);

  useEffect(() => {
    refreshBulkRunning();
    const handle = window.setInterval(refreshBulkRunning, 4000);

    return () => window.clearInterval(handle);
  }, [refreshBulkRunning]);

  const isRunning: boolean =
    !!job && (job.state === 'pending' || job.state === 'processing');
  // Gate every action on the audit OR a bulk-improve job being in flight.
  const actionsBusy: boolean = isRunning || bulkRunning;

  useEffect(() => {
    if (!isRunning) return;
    const handle = window.setInterval(() => {
      refreshJob();
    }, 4000);

    return () => window.clearInterval(handle);
  }, [isRunning, refreshJob]);

  const handleDispatch = async (): Promise<void> => {
    if (!token || dispatching) return;
    setDispatching(true);
    try {
      const fresh = await dispatchContentCheck();
      if (fresh) {
        const next = new URLSearchParams();
        next.set('job', fresh.job_id);
        setSearchParams(next, { replace: true });
      }
    } finally {
      setDispatching(false);
    }
  };

  const handleRerun = async (): Promise<void> => {
    if (!token || !job || rerunBusy) return;
    setRerunBusy(true);
    try {
      const fresh = await rerunContentCheck(job.job_id);
      if (fresh) {
        const next = new URLSearchParams();
        next.set('job', fresh.job_id);
        setSearchParams(next, { replace: true });
      }
    } finally {
      setRerunBusy(false);
    }
  };

  if (!token) {
    return (
      <div className="w-full h-full flex items-center justify-center p-12">
        <div className="w-8 h-8 border-2 border-gray-200 border-t-[#B7007C] rounded-full animate-spin" />
      </div>
    );
  }

  return (
    <div className="w-full lg:w-11/12 xl:w-10/12 mx-auto p-5 space-y-6">
      <header className="flex flex-wrap items-end justify-between gap-3">
        <div>
          <h1 className="text-2xl font-bold text-gray-900">Catalog Quality</h1>
          <p className="mt-1 text-sm text-gray-500">
            Title and description AEO/GEO readiness, scored per SKU.
          </p>
        </div>
        <div className="flex flex-wrap items-center gap-2">
          {job && (
            <button
              type="button"
              onClick={handleRerun}
              disabled={rerunBusy || actionsBusy}
              className="inline-flex h-9 items-center gap-2 rounded-full border border-gray-200 bg-white px-3 text-xs font-medium text-gray-700 transition-colors hover:border-[#B7007C]/30 hover:text-[#B7007C] disabled:opacity-60"
            >
              {rerunBusy ? 'Re-running...' : 'Re-run on same SKUs'}
            </button>
          )}
          {job && (
            <button
              type="button"
              onClick={() => setBulkScope({})}
              disabled={actionsBusy}
              className="inline-flex h-9 items-center gap-2 rounded-full border border-[#B7007C]/50 bg-white px-3 text-xs font-semibold text-[#B7007C] transition-colors hover:bg-[#B7007C]/10 disabled:opacity-60"
            >
              <Sparkles className="h-3.5 w-3.5" />
              Bulk improve
            </button>
          )}
          <button
            type="button"
            onClick={handleDispatch}
            disabled={dispatching || actionsBusy}
            className="inline-flex h-9 items-center justify-center rounded-full bg-[#B7007C] px-4 text-xs font-semibold text-white shadow-sm transition-colors hover:bg-[#900063] disabled:opacity-60"
          >
            {dispatching ? 'Dispatching...' : 'Run new check'}
          </button>
        </div>
      </header>

      {!job && (
        <div className="rounded-2xl border border-gray-200 bg-white p-10 text-center">
          <h2 className="text-lg font-semibold text-gray-900">
            No content check has been run yet
          </h2>
          <p className="mx-auto mt-2 max-w-md text-sm text-gray-500">
            Click{' '}
            <span className="font-medium text-gray-900">Run new check</span>{' '}
            above to audit 50 random products from your catalog. Each title and
            description is scored individually against AEO and GEO criteria.
          </p>
        </div>
      )}

      {job && <JobOverview job={job} />}
      {job && (job.csv_url || job.pdf_url) && (
        <div className="flex flex-wrap items-center gap-2 rounded-xl border border-gray-200 bg-white p-3 text-xs">
          <span className="text-gray-500">Download report:</span>
          {job.csv_url && (
            <a
              href={job.csv_url}
              target="_blank"
              rel="noreferrer"
              className="rounded-md border border-gray-200 bg-white px-2.5 py-1 font-medium text-gray-900 hover:bg-gray-50"
            >
              CSV
            </a>
          )}
          {job.pdf_url && (
            <a
              href={job.pdf_url}
              target="_blank"
              rel="noreferrer"
              className="rounded-md border border-gray-200 bg-white px-2.5 py-1 font-medium text-gray-900 hover:bg-gray-50"
            >
              PDF
            </a>
          )}
        </div>
      )}
      {job && (
        <ProductsTable
          jobId={job.job_id}
          products={products}
          severity={severityParam}
          language={job.language_detected || 'en'}
          disabled={actionsBusy}
          onOpenBulk={(sku, productName) => setBulkScope({ sku, productName })}
        />
      )}
      {job && (
        <BulkImproveModal
          open={bulkScope !== null}
          onOpenChange={next => !next && setBulkScope(null)}
          jobId={job.job_id}
          language={job.language_detected || 'en'}
          sku={bulkScope?.sku}
          productName={bulkScope?.productName}
        />
      )}
    </div>
  );
};

function JobOverview({ job }: { job: IContentCheckJob }): JSX.Element {
  const isCompleted: boolean = job.state === 'completed';
  const isRunning: boolean =
    job.state === 'pending' || job.state === 'processing';
  const isFailed: boolean = job.state === 'failed';

  const scoreColor: string =
    job.overall_score >= 80
      ? 'text-emerald-600'
      : job.overall_score >= 65
        ? 'text-lime-600'
        : job.overall_score >= 50
          ? 'text-amber-600'
          : job.overall_score >= 35
            ? 'text-orange-600'
            : 'text-red-600';

  const totalProducts: number = Math.max(1, job.total_products);
  const progressPercent: number = Math.max(
    0,
    Math.min(100, Math.round((job.processed_products / totalProducts) * 100))
  );

  return (
    <section className="rounded-2xl border border-gray-200 bg-white p-6">
      <div className="grid gap-6 lg:grid-cols-[1fr_auto]">
        <div>
          <div className="flex items-center gap-2 text-xs uppercase tracking-wider text-gray-500">
            <span>
              {isRunning
                ? 'Audit in progress'
                : isFailed
                  ? 'Audit failed'
                  : 'Latest audit'}
            </span>
            {job.language_detected && (
              <>
                <span className="text-gray-300">·</span>
                <span>{job.language_detected.toUpperCase()}</span>
              </>
            )}
          </div>
          {isCompleted ? (
            <>
              <div className="mt-3 flex items-baseline gap-2">
                <span
                  className={`text-6xl font-bold tabular-nums ${scoreColor}`}
                >
                  {job.overall_score}
                </span>
                <span className="text-base text-gray-500">/100</span>
              </div>
              <p className="mt-3 text-sm text-gray-500">
                Audited {job.processed_products} of {job.total_products}{' '}
                products
              </p>
            </>
          ) : isFailed ? (
            <p className="mt-3 text-sm text-gray-500">
              {job.error ?? 'Something went wrong while running this audit.'}
            </p>
          ) : (
            <div className="mt-4 max-w-md">
              <div className="flex items-baseline justify-between gap-3">
                <div className="flex items-baseline gap-1.5">
                  <span className="text-3xl font-semibold tabular-nums text-gray-900">
                    {job.processed_products}
                  </span>
                  <span className="text-sm text-gray-500">
                    / {job.total_products} products
                  </span>
                </div>
                <span className="text-sm font-medium tabular-nums text-gray-500">
                  {progressPercent}%
                </span>
              </div>
              <div className="mt-3 h-1 w-full overflow-hidden rounded-full bg-gray-100">
                <div
                  className="h-full rounded-full bg-gray-700 transition-all duration-500"
                  style={{ width: `${Math.max(2, progressPercent)}%` }}
                />
              </div>
            </div>
          )}
        </div>
        <div className="flex flex-col gap-2 lg:min-w-[220px]">
          <SeverityChip
            label="Critical"
            count={job.critical_count}
            accent="text-red-600"
            dotClass="bg-red-500"
          />
          <SeverityChip
            label="Warnings"
            count={job.warning_count}
            accent="text-amber-700"
            dotClass="bg-amber-500"
          />
          <SeverityChip
            label="OK"
            count={job.ok_count}
            accent="text-emerald-700"
            dotClass="bg-emerald-500"
          />
        </div>
      </div>
    </section>
  );
}

function SeverityChip({
  label,
  count,
  accent,
  dotClass,
}: {
  label: string;
  count: number;
  accent: string;
  dotClass: string;
}): JSX.Element {
  return (
    <div className="flex items-center justify-between rounded-lg border border-gray-200 bg-gray-50 px-3 py-2">
      <div className="flex items-center gap-2 text-sm text-gray-900">
        <span className={`inline-block h-2 w-2 rounded-full ${dotClass}`} />
        <span>{label}</span>
      </div>
      <span className={`text-sm font-semibold tabular-nums ${accent}`}>
        {count}
      </span>
    </div>
  );
}

function ProductsTable({
  jobId,
  products,
  severity,
  language,
  disabled,
  onOpenBulk,
}: {
  jobId: string;
  products: IContentCheckProductRow[];
  severity: ContentCheckSeverity | null;
  language: string;
  disabled: boolean;
  onOpenBulk: (sku?: string, productName?: string) => void;
}): JSX.Element {
  const [searchParams, setSearchParams] = useSearchParams();
  const filterTabs = useMemo(
    () =>
      [null, 'critical', 'warning', 'ok'] as Array<ContentCheckSeverity | null>,
    []
  );

  const handleSelectSeverity = (tab: ContentCheckSeverity | null): void => {
    const nextParams = new URLSearchParams(searchParams);
    nextParams.set('job', jobId);
    if (tab) nextParams.set('severity', tab);
    else nextParams.delete('severity');
    setSearchParams(nextParams);
  };

  return (
    <section className="rounded-2xl border border-gray-200 bg-white">
      <header className="flex flex-wrap items-center justify-between gap-3 border-b border-gray-200 px-5 py-3">
        <h2 className="text-sm font-semibold text-gray-900">
          Per-product results
        </h2>
        <nav className="flex items-center gap-1 text-xs">
          {filterTabs.map(tab => {
            const isActive = tab === severity;
            const styles = TAB_STYLES[tab ?? 'all'];

            return (
              <button
                key={tab ?? 'all'}
                type="button"
                onClick={() => handleSelectSeverity(tab)}
                className={`cursor-pointer rounded-md px-2.5 py-1 font-medium transition-colors ${isActive ? styles.active : styles.inactive}`}
              >
                {tab ? SEVERITY_LABELS[tab] : 'All'}
              </button>
            );
          })}
        </nav>
      </header>
      {products.length === 0 ? (
        <div className="px-5 py-10 text-center text-sm text-gray-500">
          No products to show for this filter.
        </div>
      ) : (
        <ul className="divide-y divide-gray-200">
          {products.map(product => (
            <ProductRow
              key={product.sku}
              jobId={jobId}
              product={product}
              language={language}
              disabled={disabled}
              onOpenBulk={onOpenBulk}
            />
          ))}
        </ul>
      )}
    </section>
  );
}

function ProductRow({
  jobId,
  product,
  language,
  disabled,
  onOpenBulk,
}: {
  jobId: string;
  product: IContentCheckProductRow;
  language: string;
  disabled: boolean;
  onOpenBulk: (sku?: string, productName?: string) => void;
}): JSX.Element {
  const [expanded, setExpanded] = useState<boolean>(false);
  const [actionMode, setActionMode] = useState<'gap' | 'faq' | null>(null);

  const issueCount: number =
    (product.title_issues?.length ?? 0) +
    (product.description_issues?.length ?? 0);
  const severityBadge = SEVERITY_BADGE[product.severity];

  return (
    <li className="px-5 py-3">
      <div className="flex items-center justify-between gap-4">
        <div className="min-w-0 flex-1">
          <div className="flex items-center gap-2">
            <span
              className={`rounded-md px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${severityBadge.badge}`}
            >
              {severityBadge.label}
            </span>
            <span className="truncate text-sm font-medium text-gray-900">
              {product.product_name || product.sku}
            </span>
          </div>
          <p className="mt-1 truncate text-xs text-gray-500">
            SKU {product.sku}
            {product.detected_product_type &&
              ` · ${product.detected_product_type}`}
          </p>
        </div>
        <div className="flex items-center gap-3 text-xs text-gray-500">
          <span>
            T{' '}
            <span className="tabular-nums text-gray-900">
              {product.title_score}
            </span>
          </span>
          <span>
            D{' '}
            <span className="tabular-nums text-gray-900">
              {product.description_score}
            </span>
          </span>
          {issueCount > 0 && (
            <button
              type="button"
              onClick={() => setExpanded(value => !value)}
              className="rounded-md border border-gray-200 bg-white px-2 py-1 text-xs font-medium text-gray-700 hover:bg-gray-50"
            >
              {expanded ? 'Hide' : `Show ${issueCount} issues`}
            </button>
          )}
          <div className="flex items-center gap-1.5">
            <button
              type="button"
              disabled={disabled}
              onClick={() => onOpenBulk(product.sku, product.product_name)}
              title="Open the improve modal scoped to this product and email the rewrite."
              className="inline-flex items-center gap-1.5 rounded-md bg-[#B7007C] px-2.5 py-1 text-xs font-semibold text-white shadow-sm shadow-[#B7007C]/30 transition-colors hover:bg-[#900063] disabled:pointer-events-none disabled:opacity-60"
            >
              <Mail className="h-3 w-3" />
              Replace
            </button>
            <button
              type="button"
              disabled={disabled}
              onClick={() => setActionMode('gap')}
              title="Improve the title and add the content the description is missing. Opens a preview to copy."
              className="inline-flex items-center gap-1.5 rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-medium text-gray-700 hover:bg-gray-50 disabled:pointer-events-none disabled:opacity-60"
            >
              <PlusCircle className="h-3 w-3" />
              Add missing
            </button>
            <button
              type="button"
              disabled={disabled}
              onClick={() => setActionMode('faq')}
              title="Generate an FAQ from the description (pick how many questions). Opens a preview to copy."
              className="inline-flex items-center gap-1.5 rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-medium text-gray-700 hover:bg-gray-50 disabled:pointer-events-none disabled:opacity-60"
            >
              <HelpCircle className="h-3 w-3" />
              FAQ
            </button>
          </div>
        </div>
      </div>
      {expanded && issueCount > 0 && (
        <div className="mt-3 grid gap-3 rounded-lg border border-gray-200 bg-gray-50 p-3 lg:grid-cols-2">
          <IssueList title="Title issues" issues={product.title_issues} />
          <IssueList
            title="Description issues"
            issues={product.description_issues}
          />
        </div>
      )}

      <ProductActionModal
        open={actionMode !== null}
        onOpenChange={next => !next && setActionMode(null)}
        mode={actionMode ?? 'gap'}
        jobId={jobId}
        sku={product.sku}
        productName={product.product_name}
        language={language}
        titleIssues={product.title_issues ?? []}
        descriptionIssues={product.description_issues ?? []}
      />
    </li>
  );
}

function IssueList({
  title,
  issues,
}: {
  title: string;
  issues: string[];
}): JSX.Element {
  if (!issues || issues.length === 0) {
    return (
      <div>
        <p className="text-[11px] font-semibold uppercase tracking-wider text-gray-500">
          {title}
        </p>
        <p className="mt-1 text-xs text-gray-400">No issues found.</p>
      </div>
    );
  }

  return (
    <div>
      <p className="text-[11px] font-semibold uppercase tracking-wider text-gray-500">
        {title}
      </p>
      <ul className="mt-1 space-y-1 text-xs text-gray-900">
        {issues.map((issue, issueIndex) => (
          <li key={issueIndex} className="leading-relaxed">
            <span className="text-gray-500">·</span> {issue}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default CatalogQuality;
