import { useEffect, useMemo, useRef, useState } from "react";
import { ExternalLink } from "lucide-react";
import type { ApiSettings, CampaignListItem } from "../../types";
import { getCampaignWorkspaceDelta, listCampaignsDelta, retryCampaignJobs, sendUnattemptedCampaignJobs, type CampaignSummary } from "../../api/campaigns";
import { Button } from "@govoplan/core-webui";
import { Card } from "@govoplan/core-webui";
import DataGrid, { type DataGridColumn } from "../../components/table/DataGrid";
import { DismissibleAlert } from "@govoplan/core-webui";
import { LoadingFrame } from "@govoplan/core-webui";
import { MetricCard } from "@govoplan/core-webui";
import { PageTitle } from "@govoplan/core-webui";
import { StatusBadge, i18nMessage, mergeDeltaRows, useDeltaWatermarks, useGuardedNavigate } from "@govoplan/core-webui";
import { asRecord, formatDateTime, humanize } from "../campaigns/utils/campaignView";

type OperatorRow = {
  campaign: CampaignListItem;
  summary: CampaignSummary | null;
  failed: number;
  outcomeUnknown: number;
  notAttempted: number;
  queuedOrActive: number;
  imapFailed: number;
  queueable: number;
  needsAttention: number;
};

export default function OperatorQueuePage({ settings }: {settings: ApiSettings;}) {
  const navigate = useGuardedNavigate();
  const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks();
  const [rows, setRows] = useState<OperatorRow[]>([]);
  const campaignsRef = useRef<CampaignListItem[]>([]);
  const summariesRef = useRef<Record<string, CampaignSummary | null>>({});
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [message, setMessage] = useState("");
  const [busy, setBusy] = useState("");

  const settingsKey = useMemo(
    () => JSON.stringify({
      apiBaseUrl: settings.apiBaseUrl,
      apiKey: settings.apiKey,
      accessToken: settings.accessToken
    }),
    [settings.apiBaseUrl, settings.apiKey, settings.accessToken]
  );

  useEffect(() => {
    campaignsRef.current = [];
    summariesRef.current = {};
    resetDeltaWatermark();
    void load();
  }, [settingsKey, resetDeltaWatermark]);

  async function load() {
    setLoading(true);
    setError("");
    try {
      const campaigns = await loadCampaignsDelta();
      const summaries = await loadCampaignSummariesDelta(campaigns);
      setRows(campaigns.map((campaign) => toRow(campaign, summaries[campaign.id] ?? null)));
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setLoading(false);
    }
  }

  async function runAction(row: OperatorRow, action: "retry" | "unattempted") {
    const key = `${action}:${row.campaign.id}`;
    setBusy(key);
    setError("");
    setMessage("");
    try {
      const response = action === "retry" ?
      await retryCampaignJobs(settings, row.campaign.id, { enqueue_celery: true }) :
      await sendUnattemptedCampaignJobs(settings, row.campaign.id, { enqueue_celery: true });
      const result = asRecord(response.result ?? response);
      setMessage(i18nMessage("i18n:govoplan-campaign.value_value_value_enqueued.35b33f6d", { value0: row.campaign.name, value1: humanize(String(result.action ?? action)), value2: String(result.enqueued_count ?? 0) }));
      resetDeltaWatermark(operatorCampaignSummaryKey(row.campaign.id));
      await load();
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusy("");
    }
  }

  async function loadCampaignsDelta(): Promise<CampaignListItem[]> {
    const key = operatorCampaignListKey();
    let nextWatermark = getDeltaWatermark(key);
    let campaigns = campaignsRef.current;
    let hasMore = false;
    do {
      const response = await listCampaignsDelta(settings, { since: nextWatermark });
      campaigns = mergeDeltaRows(campaigns, response.campaigns, response.deleted, (campaign) => campaign.id, {
        deletedResourceType: "campaign",
        sort: sortCampaignsByUpdatedDesc
      });
      nextWatermark = response.watermark ?? null;
      hasMore = response.has_more;
    } while (hasMore);
    const campaignIds = new Set(campaigns.map((campaign) => campaign.id));
    for (const campaignId of Object.keys(summariesRef.current)) {
      if (!campaignIds.has(campaignId)) delete summariesRef.current[campaignId];
    }
    campaignsRef.current = campaigns;
    setDeltaWatermark(key, nextWatermark);
    return campaigns;
  }

  async function loadCampaignSummariesDelta(campaigns: CampaignListItem[]): Promise<Record<string, CampaignSummary | null>> {
    const summaries = { ...summariesRef.current };
    await Promise.all(campaigns.map(async (campaign) => {
      const key = operatorCampaignSummaryKey(campaign.id);
      let nextWatermark = getDeltaWatermark(key);
      let summary = summaries[campaign.id] ?? null;
      let hasMore = false;
      try {
        do {
          const response = await getCampaignWorkspaceDelta(settings, campaign.id, {
            includeCurrentVersion: false,
            includeVersions: false,
            includeSummary: true,
            since: nextWatermark
          });
          if (response.full || response.summary) summary = response.summary;
          nextWatermark = response.watermark ?? null;
          hasMore = response.has_more;
        } while (hasMore);
        setDeltaWatermark(key, nextWatermark);
        summaries[campaign.id] = summary;
      } catch {
        summaries[campaign.id] = summary;
      }
    }));
    summariesRef.current = summaries;
    return summaries;
  }

  function operatorCampaignListKey(): string {
    return JSON.stringify({ scope: "operator-campaigns", settingsKey });
  }

  function operatorCampaignSummaryKey(campaignId: string): string {
    return JSON.stringify({ scope: "operator-campaign-summary", campaignId, settingsKey });
  }

  const totals = rows.reduce((acc, row) => ({
    failed: acc.failed + row.failed,
    outcomeUnknown: acc.outcomeUnknown + row.outcomeUnknown,
    notAttempted: acc.notAttempted + row.notAttempted,
    queuedOrActive: acc.queuedOrActive + row.queuedOrActive,
    imapFailed: acc.imapFailed + row.imapFailed
  }), { failed: 0, outcomeUnknown: 0, notAttempted: 0, queuedOrActive: 0, imapFailed: 0 });

  const columns = useMemo<DataGridColumn<OperatorRow>[]>(() => [
  { id: "campaign", header: "i18n:govoplan-campaign.campaign.69390e16", width: "minmax(260px, 1.2fr)", sticky: "start", sortable: true, filterable: true, value: (row) => row.campaign.name },
  { id: "status", header: "i18n:govoplan-campaign.status.bae7d5be", width: 145, sortable: true, filterable: true, render: (row) => <StatusBadge status={row.campaign.status} />, value: (row) => row.campaign.status },
  { id: "attention", header: "i18n:govoplan-campaign.attention.74e0b9c8", width: 120, align: "right", sortable: true, filterType: "integer", value: (row) => row.needsAttention },
  { id: "failed", header: "i18n:govoplan-campaign.failed.09fef5d8", width: 100, align: "right", sortable: true, filterType: "integer", value: (row) => row.failed },
  { id: "unknown", header: "i18n:govoplan-campaign.unknown.bc7819b3", width: 110, align: "right", sortable: true, filterType: "integer", value: (row) => row.outcomeUnknown },
  { id: "unattempted", header: "i18n:govoplan-campaign.unattempted.e7411dd6", width: 130, align: "right", sortable: true, filterType: "integer", value: (row) => row.notAttempted },
  { id: "queued", header: "i18n:govoplan-campaign.queued_active.b08bef73", width: 135, align: "right", sortable: true, filterType: "integer", value: (row) => row.queuedOrActive },
  { id: "updated", header: "i18n:govoplan-campaign.updated.f2f8570d", width: 180, sortable: true, filterType: "date", value: (row) => formatDateTime(row.campaign.updated_at), sortValue: (row) => row.campaign.updated_at ?? "" },
  {
    id: "actions",
    header: "i18n:govoplan-campaign.actions.c3cd636a",
    width: 270,
    sticky: "end",
    render: (row) =>
    <div className="button-row compact-actions">
          <Button className="admin-icon-button" onClick={() => navigate(`/campaigns/${row.campaign.id}/report`)} aria-label={i18nMessage("i18n:govoplan-campaign.open_queue_for_value.804fbed9", { value0: row.campaign.name })} title={i18nMessage("i18n:govoplan-campaign.open_queue_for_value.804fbed9", { value0: row.campaign.name })}>
            <ExternalLink />
          </Button>
          <Button onClick={() => void runAction(row, "retry")} disabled={row.failed <= 0 || Boolean(busy)}>i18n:govoplan-campaign.retry.9f5cd8a2</Button>
          <Button onClick={() => void runAction(row, "unattempted")} disabled={row.notAttempted <= 0 || Boolean(busy)}>i18n:govoplan-campaign.queue_unsent.b0e98610</Button>
        </div>

  }],
  [busy, navigate]);

  return (
    <div className="content-pad workspace-data-page">
      <div className="page-heading split workspace-heading">
        <div>
          <PageTitle loading={loading}>i18n:govoplan-campaign.operator_queue.72492fb5</PageTitle>
          <p>i18n:govoplan-campaign.queue_state_retry_candidates_and_reconciliation_.1b592bbe</p>
        </div>
        <div className="button-row compact-actions">
          <Button onClick={() => void load()} disabled={loading}>i18n:govoplan-campaign.refresh.56e3badc</Button>
        </div>
      </div>

      {error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
      {message && <DismissibleAlert tone="success" resetKey={message} floating>{message}</DismissibleAlert>}

      <section className="queue-pressure-section" aria-labelledby="operator-queue-pressure-title">
        <h2 id="operator-queue-pressure-title" className="queue-pressure-heading">i18n:govoplan-campaign.queue_pressure.28eee15e</h2>
        <QueuePressureGrid items={[
        { label: "i18n:govoplan-campaign.failed.09fef5d8", value: totals.failed, tone: "danger" },
        { label: "i18n:govoplan-campaign.outcome_unknown.6e929fca", value: totals.outcomeUnknown, tone: "warning" },
        { label: "i18n:govoplan-campaign.unattempted.e7411dd6", value: totals.notAttempted, tone: "info" },
        { label: "i18n:govoplan-campaign.queued_active.b08bef73", value: totals.queuedOrActive, tone: "neutral" },
        { label: "i18n:govoplan-campaign.imap_failed.50dbca55", value: totals.imapFailed, tone: "warning" }]
        } />
      </section>

      <LoadingFrame loading={loading} label="i18n:govoplan-campaign.loading_operator_queue.ae6576e0">
        <Card title="i18n:govoplan-campaign.campaign_queues.657785f4">
          <DataGrid
            id="operator-queue-campaigns"
            rows={rows}
            columns={columns}
            getRowKey={(row) => row.campaign.id}
            emptyText="i18n:govoplan-campaign.no_accessible_campaigns.9e080190"
            className="data-table compact-table" />
          
        </Card>
      </LoadingFrame>
    </div>);

}

function toRow(campaign: CampaignListItem, summary: CampaignSummary | null): OperatorRow {
  const cards = asRecord(summary?.cards);
  return {
    campaign,
    summary,
    failed: numberValue(cards.failed),
    outcomeUnknown: numberValue(cards.outcome_unknown),
    notAttempted: numberValue(cards.not_attempted),
    queuedOrActive: numberValue(cards.queued_or_active),
    imapFailed: numberValue(cards.imap_failed),
    queueable: numberValue(cards.queueable),
    needsAttention: numberValue(cards.needs_attention)
  };
}

function numberValue(value: unknown): number {
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
}

function sortCampaignsByUpdatedDesc(left: CampaignListItem, right: CampaignListItem): number {
  return String(right.updated_at ?? right.updatedAt ?? right.created_at ?? "").localeCompare(
    String(left.updated_at ?? left.updatedAt ?? left.created_at ?? "")
  );
}

function QueuePressureGrid({ items }: {items: Array<{label: string;value: number;tone: "neutral" | "good" | "warning" | "danger" | "info";}>;}) {
  return (
    <div className="metric-grid queue-pressure-grid">
      {items.map((item) =>
      <MetricCard key={item.label} label={item.label} value={item.value} tone={item.tone} />
      )}
    </div>);

}
