"use client";

/**
 * ForecastExportButtons — export/share control for the §22 forecast (EPIC #959).
 *
 * Rendered inside Section6Forecast's ForecastReady (forecast status === "ready").
 * Click-triggered download/share ACTIONS — imperative fetch in the handler is the
 * right call here (TanStack Query is for declarative data, not user-initiated
 * downloads), mirroring ExportButtons.
 *
 * Backend (live on prod):
 *   GET /api/v1/parcels/{cad}/forecast/export?format=md   → text/markdown attachment
 *   GET /api/v1/parcels/{cad}/forecast/export?format=json → application/json attachment
 *   GET /api/v1/parcels/{cad}/forecast/export?format=docx → Word (.docx) attachment
 *   GET /api/v1/parcels/{cad}/forecast/export?format=pptx → PowerPoint (.pptx) attachment
 *   GET /api/v1/parcels/{cad}/forecast/export?format=tg   → text/plain inline (TG snippet)
 *   No forecast run → 404 → inline «Отчёт ещё не готов» (no crash, no console spam).
 *
 * docx/pptx are binary zip-based OOXML — they go through the SAME response.blob()
 * path as md/json (never response.text(), which would corrupt the archive).
 */

import { useEffect, useRef, useState } from "react";
import { Download, Copy, Check, FileText, Presentation } from "lucide-react";

import { API_BASE_URL } from "@/lib/api";
import { triggerDownload } from "@/lib/download";

interface Props {
  cad: string;
}

// Binary OOXML formats (docx/pptx) share md/json's blob download path; only the
// query param + filename extension differ, so one discriminant covers them all.
type ExportFormat = "md" | "json" | "docx" | "pptx";

const NOT_READY_MSG = "Отчёт ещё не готов";
const GENERIC_ERR_MSG = "Не удалось получить отчёт";
const COPIED_FEEDBACK_MS = 2000;

// Cadastral numbers contain ":" (e.g. 66:41:0304002:123) — illegal / awkward in
// filenames on some OSes. Replace with "_" for the download name only.
function sanitizeCad(cad: string): string {
  return cad.replace(/:/g, "_");
}

function exportUrl(cad: string, format: ExportFormat | "tg"): string {
  return `${API_BASE_URL}/api/v1/parcels/${encodeURIComponent(
    cad,
  )}/forecast/export?format=${format}`;
}

// Writes text to the clipboard with a graceful fallback for insecure contexts
// (HTTP / older browsers) where navigator.clipboard is undefined: an off-screen
// textarea + execCommand("copy"). Returns false if every path fails.
async function copyText(text: string): Promise<boolean> {
  if (typeof navigator !== "undefined" && navigator.clipboard) {
    try {
      await navigator.clipboard.writeText(text);
      return true;
    } catch {
      // fall through to legacy path
    }
  }
  if (typeof document === "undefined") return false;
  try {
    const ta = document.createElement("textarea");
    ta.value = text;
    ta.setAttribute("readonly", "");
    ta.style.position = "absolute";
    ta.style.left = "-9999px";
    document.body.appendChild(ta);
    ta.select();
    const ok = document.execCommand("copy");
    document.body.removeChild(ta);
    return ok;
  } catch {
    return false;
  }
}

export function ForecastExportButtons({ cad }: Props) {
  const [downloadingFormat, setDownloadingFormat] =
    useState<ExportFormat | null>(null);
  const [tgLoading, setTgLoading] = useState(false);
  const [copied, setCopied] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Hold the «Скопировано» timer so it's cleared on unmount (no setState on a
  // dead component) — this is timer cleanup, not an HTTP effect.
  const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  useEffect(() => {
    return () => {
      if (copiedTimer.current) clearTimeout(copiedTimer.current);
    };
  }, []);

  async function handleDownload(format: ExportFormat) {
    setDownloadingFormat(format);
    setError(null);
    try {
      const res = await fetch(exportUrl(cad, format));
      if (!res.ok) {
        setError(res.status === 404 ? NOT_READY_MSG : GENERIC_ERR_MSG);
        return;
      }
      const blob = await res.blob();
      triggerDownload(blob, `gendesign_forecast_${sanitizeCad(cad)}.${format}`);
    } catch {
      setError(GENERIC_ERR_MSG);
    } finally {
      setDownloadingFormat(null);
    }
  }

  async function handleCopyTelegram() {
    setTgLoading(true);
    setError(null);
    try {
      const res = await fetch(exportUrl(cad, "tg"));
      if (!res.ok) {
        setError(res.status === 404 ? NOT_READY_MSG : GENERIC_ERR_MSG);
        return;
      }
      const text = await res.text();
      const ok = await copyText(text);
      if (!ok) {
        setError("Не удалось скопировать — выделите текст вручную");
        return;
      }
      setCopied(true);
      if (copiedTimer.current) clearTimeout(copiedTimer.current);
      copiedTimer.current = setTimeout(
        () => setCopied(false),
        COPIED_FEEDBACK_MS,
      );
    } catch {
      setError(GENERIC_ERR_MSG);
    } finally {
      setTgLoading(false);
    }
  }

  const mdLoading = downloadingFormat === "md";
  const jsonLoading = downloadingFormat === "json";
  const docxLoading = downloadingFormat === "docx";
  const pptxLoading = downloadingFormat === "pptx";
  const anyLoading = downloadingFormat !== null || tgLoading;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
        <ExportButton
          label="Скачать отчёт .md"
          loadingLabel="Загрузка…"
          loading={mdLoading}
          disabled={anyLoading}
          ariaLabel="Скачать отчёт прогноза в формате Markdown"
          onClick={() => void handleDownload("md")}
          icon={<Download size={14} strokeWidth={1.5} />}
        />

        <ExportButton
          label={copied ? "Скопировано" : "Скопировать для Telegram"}
          loadingLabel="Загрузка…"
          loading={tgLoading}
          disabled={anyLoading}
          ariaLabel="Скопировать сводку прогноза для Telegram"
          onClick={() => void handleCopyTelegram()}
          icon={
            copied ? (
              <Check size={14} strokeWidth={1.5} />
            ) : (
              <Copy size={14} strokeWidth={1.5} />
            )
          }
        />

        <ExportButton
          label="Скачать .json"
          loadingLabel="Загрузка…"
          loading={jsonLoading}
          disabled={anyLoading}
          ariaLabel="Скачать отчёт прогноза в формате JSON"
          onClick={() => void handleDownload("json")}
          icon={<Download size={14} strokeWidth={1.5} />}
        />

        <ExportButton
          label="Скачать .docx"
          loadingLabel="Загрузка…"
          loading={docxLoading}
          disabled={anyLoading}
          ariaLabel="Скачать отчёт прогноза в формате Word (.docx)"
          onClick={() => void handleDownload("docx")}
          icon={<FileText size={14} strokeWidth={1.5} />}
        />

        <ExportButton
          label="Скачать .pptx"
          loadingLabel="Загрузка…"
          loading={pptxLoading}
          disabled={anyLoading}
          ariaLabel="Скачать отчёт прогноза в формате PowerPoint (.pptx)"
          onClick={() => void handleDownload("pptx")}
          icon={<Presentation size={14} strokeWidth={1.5} />}
        />
      </div>

      {error && (
        <p
          style={{
            margin: 0,
            fontSize: 12,
            color: "var(--danger)",
          }}
          role="alert"
        >
          {error}
        </p>
      )}
    </div>
  );
}

// ── ExportButton (secondary / orange CTA) ────────────────────────────────────

function ExportButton({
  label,
  loadingLabel,
  loading,
  disabled,
  ariaLabel,
  onClick,
  icon,
}: {
  label: string;
  loadingLabel: string;
  loading: boolean;
  disabled: boolean;
  ariaLabel: string;
  onClick: () => void;
  icon: React.ReactNode;
}) {
  const inactive = loading || disabled;
  return (
    <button
      onClick={onClick}
      disabled={inactive}
      aria-label={ariaLabel}
      style={{
        display: "inline-flex",
        alignItems: "center",
        gap: 8,
        padding: "6px 12px",
        height: 32,
        background: inactive ? "var(--bg-card-alt)" : "var(--accent-2)",
        color: inactive ? "var(--fg-tertiary)" : "#fff",
        border: "none",
        borderRadius: 6,
        fontSize: 13,
        fontWeight: 500,
        cursor: inactive ? "not-allowed" : "pointer",
        opacity: inactive ? 0.6 : 1,
        transition: "opacity 150ms",
      }}
    >
      {icon}
      {loading ? loadingLabel : label}
    </button>
  );
}
