import { useEffect, useMemo, useState } from "react";
import type { ApiSettings } from "../../types";
import {
  downloadCampaignJobsCsv,
  emailCampaignReport,
  getCampaignJobDetail,
  getCampaignJobs,
  resolveCampaignJobOutcome,
  retryCampaignJobs,
  sendUnattemptedCampaignJobs,
  type CampaignJobDetailResponse,
  type CampaignJobsResponse,
} from "../../api/campaigns";
import { Card } from "@govoplan/core-webui";
import { Button } from "@govoplan/core-webui";
import { ConfirmDialog } from "@govoplan/core-webui";
import DataGrid, { type DataGridColumn, type DataGridListOption } from "../../components/table/DataGrid";
import { Dialog } from "@govoplan/core-webui";
import { DismissibleAlert } from "@govoplan/core-webui";
import { PageTitle } from "@govoplan/core-webui";
import { StatusBadge } from "@govoplan/core-webui";
import VersionLine from "./components/VersionLine";
import { LoadingFrame } from "@govoplan/core-webui";
import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
import { asRecord, formatDateTime, humanize, stringifyPreview } from "./utils/campaignView";

const SEND_STATUS_OPTIONS: DataGridListOption[] = [
  "not_queued",
  "queued",
  "claimed",
  "sending",
  "smtp_accepted",
  "sent",
  "outcome_unknown",
  "failed_temporary",
  "failed_permanent",
  "cancelled",
].map((value) => ({ value, label: humanize(value) }));

type ReconcileRequest = { jobId: string; decision: "smtp_accepted" | "not_sent" } | null;

export default function CampaignReportPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
  const { data, loading, error, reload } = useCampaignWorkspaceData(settings, campaignId, { includeSummary: true });
  const version = data.currentVersion;
  const cards = data.summary?.cards;
  const delivery = asRecord(data.summary?.delivery);
  const rateLimit = asRecord(delivery.rate_limit);
  const imapPolicy = asRecord(delivery.imap_append_sent);

  const [jobs, setJobs] = useState<CampaignJobsResponse>({
    jobs: [],
    page: 1,
    page_size: 50,
    total: 0,
    total_unfiltered: 0,
    pages: 0,
    counts: {},
    filtered_counts: {},
    review: {},
  });
  const [jobsLoading, setJobsLoading] = useState(false);
  const [page, setPage] = useState(1);
  const [sendStatus, setSendStatus] = useState("");
  const [query, setQuery] = useState("");
  const [appliedQuery, setAppliedQuery] = useState("");
  const [actionMessage, setActionMessage] = useState("");
  const [actionError, setActionError] = useState("");
  const [busyAction, setBusyAction] = useState("");
  const [detail, setDetail] = useState<CampaignJobDetailResponse | null>(null);
  const [emailOpen, setEmailOpen] = useState(false);
  const [emailRecipients, setEmailRecipients] = useState("");
  const [attachCsv, setAttachCsv] = useState(true);
  const [attachJson, setAttachJson] = useState(false);
  const [reconcile, setReconcile] = useState<ReconcileRequest>(null);

  useEffect(() => {
    const handle = window.setTimeout(() => {
      setAppliedQuery(query.trim());
      setPage(1);
    }, 350);
    return () => window.clearTimeout(handle);
  }, [query]);

  useEffect(() => {
    void loadJobs();
  }, [campaignId, version?.id, page, sendStatus, appliedQuery]);

  async function loadJobs() {
    if (!campaignId) return;
    setJobsLoading(true);
    setActionError("");
    try {
      const response = await getCampaignJobs(settings, campaignId, {
        versionId: version?.id,
        page,
        pageSize: 50,
        sendStatus: sendStatus ? [sendStatus] : undefined,
        query: appliedQuery || undefined,
      });
      setJobs(response);
      if (response.pages > 0 && page > response.pages) setPage(response.pages);
    } catch (err) {
      setActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setJobsLoading(false);
    }
  }

  async function reloadAll() {
    await Promise.all([reload(), loadJobs()]);
  }

  async function runExplicitAction(action: "retry" | "unattempted") {
    if (!version || busyAction) return;
    setBusyAction(action);
    setActionError("");
    setActionMessage("");
    try {
      const response = action === "retry"
        ? await retryCampaignJobs(settings, campaignId, { version_id: version.id, enqueue_celery: true })
        : await sendUnattemptedCampaignJobs(settings, campaignId, { version_id: version.id, enqueue_celery: true });
      const result = asRecord(response.result ?? response);
      setActionMessage(`${humanize(String(result.action ?? action))}: ${String(result.selected_count ?? 0)} job(s) selected, ${String(result.enqueued_count ?? 0)} enqueued.`);
      await reloadAll();
    } catch (err) {
      setActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusyAction("");
    }
  }

  async function reconcileOutcome() {
    if (!reconcile || busyAction) return;
    setBusyAction("reconcile");
    setActionError("");
    try {
      await resolveCampaignJobOutcome(settings, campaignId, reconcile.jobId, reconcile.decision);
      setActionMessage(reconcile.decision === "smtp_accepted"
        ? "The job was recorded as SMTP accepted and is protected from retry."
        : "The job was recorded as not sent. It is now an explicit retry candidate.");
      setReconcile(null);
      await reloadAll();
    } catch (err) {
      setActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusyAction("");
    }
  }

  async function openJob(jobId: string) {
    setBusyAction("detail");
    setActionError("");
    try {
      setDetail(await getCampaignJobDetail(settings, campaignId, jobId));
    } catch (err) {
      setActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusyAction("");
    }
  }

  async function exportCsv() {
    setBusyAction("csv");
    setActionError("");
    try {
      await downloadCampaignJobsCsv(settings, campaignId, version?.id);
      setActionMessage("Campaign job CSV downloaded.");
    } catch (err) {
      setActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusyAction("");
    }
  }

  async function sendReportEmail() {
    const recipients = emailRecipients.split(/[;,\n]+/).map((value) => value.trim()).filter(Boolean);
    if (recipients.length === 0 || busyAction) return;
    setBusyAction("email");
    setActionError("");
    try {
      await emailCampaignReport(settings, campaignId, {
        to: recipients,
        version_id: version?.id,
        include_jobs: false,
        attach_jobs_csv: attachCsv,
        attach_report_json: attachJson,
      });
      setActionMessage(`Report sent to ${recipients.join(", ")}.`);
      setEmailOpen(false);
    } catch (err) {
      setActionError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusyAction("");
    }
  }

  const columns = useMemo<DataGridColumn<Record<string, unknown>>[]>(() => [
    { id: "number", header: "#", width: 70, sticky: "start", sortable: true, value: (row) => Number(row.entry_index ?? 0) + 1 },
    { id: "recipient", header: "Recipient", width: 240, resizable: true, sortable: true, filterable: true, value: (row) => String(row.recipient_email ?? "—") },
    { id: "subject", header: "Subject", width: "minmax(260px, 1fr)", resizable: true, sortable: true, filterable: true, value: (row) => String(row.subject ?? "—") },
    { id: "validation", header: "Validation", width: 145, sortable: true, filterable: true, render: (row) => <StatusBadge status={String(row.validation_status ?? "unknown")} />, value: (row) => String(row.validation_status ?? "unknown") },
    { id: "send", header: "SMTP", width: 160, sortable: true, filterable: true, columnType: "from-list", list: { options: SEND_STATUS_OPTIONS, display: "pill" }, value: (row) => String(row.send_status ?? "unknown") },
    { id: "imap", header: "IMAP", width: 130, sortable: true, filterable: true, render: (row) => <StatusBadge status={String(row.imap_status ?? "unknown")} />, value: (row) => String(row.imap_status ?? "unknown") },
    { id: "attempts", header: "Attempts", width: 105, align: "right", sortable: true, filterType: "integer", value: (row) => Number(row.attempt_count ?? 0) },
    { id: "error", header: "Last result", width: "minmax(220px, 1fr)", resizable: true, filterable: true, value: (row) => String(row.last_error ?? "—") },
    {
      id: "actions",
      header: "Actions",
      width: 250,
      sticky: "end",
      render: (row) => {
        const id = String(row.id ?? "");
        const status = String(row.send_status ?? "");
        return (
          <div className="button-row compact-actions">
            <Button onClick={() => void openJob(id)} disabled={!id || busyAction === "detail"}>Details</Button>
            {status === "outcome_unknown" && <Button onClick={() => setReconcile({ jobId: id, decision: "smtp_accepted" })}>Accepted</Button>}
            {status === "outcome_unknown" && <Button onClick={() => setReconcile({ jobId: id, decision: "not_sent" })}>Not sent</Button>}
          </div>
        );
      },
    },
  ], [busyAction]);

  return (
    <div className="content-pad workspace-data-page">
      <div className="page-heading split workspace-heading">
        <div>
          <PageTitle loading={loading}>Report</PageTitle>
          <VersionLine version={version} versions={data.versions} loadedAt={version?.updated_at ?? data.summary?.generated_at} />
        </div>
        <div className="button-row compact-actions">
          <Button onClick={() => void exportCsv()} disabled={busyAction === "csv"}>Download CSV</Button>
          <Button onClick={() => setEmailOpen(true)}>Email report</Button>
          <Button onClick={() => void reloadAll()} disabled={loading || jobsLoading}>Reload</Button>
        </div>
      </div>
      {(error || actionError) && <DismissibleAlert tone="danger" resetKey={`${error}${actionError}`} floating>{error || actionError}</DismissibleAlert>}
      {actionMessage && <DismissibleAlert tone="success" resetKey={actionMessage} floating>{actionMessage}</DismissibleAlert>}
      <LoadingFrame loading={loading} label="Loading report data…">
        <div className="dashboard-grid">
          <Card title="Delivery outcome">
            <dl className="detail-list">
              <div><dt>Generated</dt><dd>{formatDateTime(data.summary?.generated_at)}</dd></div>
              <div><dt>Jobs total</dt><dd>{cards?.jobs_total ?? "—"}</dd></div>
              <div><dt>SMTP accepted</dt><dd>{cards?.smtp_accepted ?? cards?.sent ?? 0}</dd></div>
              <div><dt>Failed</dt><dd>{cards?.failed ?? 0}</dd></div>
              <div><dt>Outcome unknown</dt><dd>{cards?.outcome_unknown ?? 0}</dd></div>
              <div><dt>Not attempted</dt><dd>{cards?.not_attempted ?? 0}</dd></div>
              <div><dt>Cancelled</dt><dd>{cards?.cancelled ?? 0}</dd></div>
            </dl>
          </Card>
          <Card title="IMAP and execution plan">
            <dl className="detail-list">
              <div><dt>IMAP appended</dt><dd>{cards?.imap_appended ?? 0}</dd></div>
              <div><dt>IMAP failed</dt><dd>{cards?.imap_failed ?? 0}</dd></div>
              <div><dt>Append policy</dt><dd>{imapPolicy.enabled === true ? `Enabled (${String(imapPolicy.folder ?? "auto")})` : "Disabled"}</dd></div>
              <div><dt>Rate limit</dt><dd>{rateLimit.messages_per_minute ? `${String(rateLimit.messages_per_minute)} / minute` : "—"}</dd></div>
              <div><dt>Minimum remaining duration</dt><dd>{String(delivery.estimated_remaining_send_human ?? "—")}</dd></div>
              <div><dt>Execution snapshot</dt><dd title={String(delivery.execution_snapshot_hash ?? "")}>{delivery.execution_snapshot_hash ? `${String(delivery.execution_snapshot_hash).slice(0, 12)}…` : "Missing"}</dd></div>
            </dl>
          </Card>
          <Card title="Explicit delivery actions">
            <p className="muted">These actions never include SMTP-accepted or unresolved jobs. Uncertain outcomes must be reconciled first.</p>
            <div className="button-row compact-actions">
              <Button onClick={() => void runExplicitAction("retry")} disabled={!version || Boolean(busyAction)}>Retry temporary failures</Button>
              <Button onClick={() => void runExplicitAction("unattempted")} disabled={!version || Boolean(busyAction)}>Send unattempted jobs</Button>
            </div>
          </Card>
        </div>

        <Card title="Recipient delivery jobs">
          <div className="page-heading split">
            <div className="button-row compact-actions">
              <input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="Search recipient, subject or entry ID" />
              <select value={sendStatus} onChange={(event) => { setSendStatus(event.target.value); setPage(1); }}>
                <option value="">All SMTP states</option>
                {SEND_STATUS_OPTIONS.map((option) => <option key={option.value} value={option.value}>{option.label}</option>)}
              </select>
            </div>
            <span className="muted">{jobs.total} matching of {jobs.total_unfiltered} total job(s)</span>
          </div>
          <LoadingFrame loading={jobsLoading} label="Loading delivery jobs…">
            <DataGrid<Record<string, unknown>>
              id={`campaign-report-jobs-${campaignId}`}
              rows={jobs.jobs}
              columns={columns}
              getRowKey={(row: Record<string, unknown>) => String(row.id ?? "")}
              emptyText="No jobs match the current filters."
            />
          </LoadingFrame>
          <div className="button-row compact-actions">
            <Button onClick={() => setPage((value) => Math.max(1, value - 1))} disabled={page <= 1 || jobsLoading}>Previous</Button>
            <span>Page {jobs.pages === 0 ? 0 : jobs.page} of {jobs.pages}</span>
            <Button onClick={() => setPage((value) => Math.min(jobs.pages || 1, value + 1))} disabled={page >= jobs.pages || jobsLoading}>Next</Button>
          </div>
        </Card>
      </LoadingFrame>

      <Dialog
        open={emailOpen}
        title="Email campaign report"
        onClose={() => setEmailOpen(false)}
        closeDisabled={busyAction === "email"}
        footer={(
          <div className="button-row">
            <Button onClick={() => setEmailOpen(false)} disabled={busyAction === "email"}>Cancel</Button>
            <Button variant="primary" onClick={() => void sendReportEmail()} disabled={!emailRecipients.trim() || busyAction === "email"}>Send report</Button>
          </div>
        )}
      >
        <label className="field-stack">
          <span>Recipients</span>
          <textarea value={emailRecipients} onChange={(event) => setEmailRecipients(event.target.value)} placeholder="audit@example.org; owner@example.org" rows={3} />
        </label>
        <label><input type="checkbox" checked={attachCsv} onChange={(event) => setAttachCsv(event.target.checked)} /> Attach job CSV</label>
        <label><input type="checkbox" checked={attachJson} onChange={(event) => setAttachJson(event.target.checked)} /> Attach JSON report</label>
      </Dialog>

      <Dialog
        open={Boolean(detail)}
        title="Campaign job detail"
        onClose={() => setDetail(null)}
        className="dialog-panel-wide"
      >
        {detail && (
          <div className="stacked-sections">
            <dl className="detail-list">
              <div><dt>Recipient</dt><dd>{String(detail.job.recipient_email ?? "—")}</dd></div>
              <div><dt>Subject</dt><dd>{String(detail.job.subject ?? "—")}</dd></div>
              <div><dt>SMTP state</dt><dd><StatusBadge status={String(detail.job.send_status ?? "unknown")} /></dd></div>
              <div><dt>IMAP state</dt><dd><StatusBadge status={String(detail.job.imap_status ?? "unknown")} /></dd></div>
              <div><dt>Attachments</dt><dd>{String(detail.job.matched_file_count ?? detail.job.attachment_count ?? 0)}</dd></div>
            </dl>
            <h3>SMTP attempts</h3>
            <pre className="json-preview">{stringifyPreview(detail.attempts.smtp ?? [], 12000)}</pre>
            <h3>IMAP attempts</h3>
            <pre className="json-preview">{stringifyPreview(detail.attempts.imap ?? [], 12000)}</pre>
          </div>
        )}
      </Dialog>

      <ConfirmDialog
        open={Boolean(reconcile)}
        title={reconcile?.decision === "smtp_accepted" ? "Record SMTP acceptance?" : "Record message as not sent?"}
        message={reconcile?.decision === "smtp_accepted"
          ? "Use this only after checking the SMTP server or recipient-side evidence. The job will be protected from retry and may proceed to IMAP append."
          : "Use this only when you have evidence that SMTP did not accept the message. The job will become a failed-temporary retry candidate, but it will not be retried automatically."}
        confirmLabel={reconcile?.decision === "smtp_accepted" ? "Record accepted" : "Record not sent"}
        tone={reconcile?.decision === "smtp_accepted" ? "default" : "danger"}
        busy={busyAction === "reconcile"}
        onConfirm={() => void reconcileOutcome()}
        onCancel={() => setReconcile(null)}
      />
    </div>
  );
}
