import { useMemo, useState } from "react";
import type { Options, AlignedData } from "uplot";
import type { SolveResponse, SolverOutput } from "../../types";
import type { CaseState } from "../../state/caseModel";
import type { EngineeringChecks } from "../../state/engineeringChecks";
import { buildTrajectorySegments } from "../../state/trajectoryMetrics";
import { Fieldset } from "../common/Fieldset";
import { UPlotChart } from "../common/UPlotChart";
import { Wellbore3DView } from "../common/Wellbore3DView";
import type { OverlayMode } from "../common/Wellbore3DView";

type Props = {
  result: SolveResponse | null;
  loading: boolean;
  error: string | null;
  lastRunAt: string | null;
  elapsedSeconds: number | null;
  caseState: CaseState;
  checks: EngineeringChecks;
  onNavigateTab?: (tabId: string) => void;
};

type CardSeries = {
  position: number[];
  polished: number[];
  downhole: number[];
};

function toSeries(solver: SolverOutput | undefined): CardSeries | null {
  if (!solver || !solver.card?.length) return null;
  return {
    position: solver.card.map((p) => p.position),
    polished: solver.card.map((p) => p.polishedLoad),
    downhole: solver.card.map((p) => p.downholeLoad)
  };
}

function formatKn(value: number | undefined): string {
  if (value === undefined || !Number.isFinite(value)) return "—";
  return (value / 1000).toFixed(2);
}

function computePumpPlacement(caseState: CaseState) {
  const segments = buildTrajectorySegments(caseState.survey);
  const taperLen = caseState.taper.reduce((sum, row) => sum + Math.max(0, row.length), 0);
  if (!segments.length) {
    return {
      nearestStationIndex: -1,
      nearestStationMd: null as number | null,
      nearestStationDistance: null as number | null,
      surveyEndMd: null as number | null,
      surveyToPumpDelta: null as number | null,
      rodToPumpDelta: taperLen - caseState.pumpDepth
    };
  }
  const stationMds = [segments[0].a.md, ...segments.map((s) => s.b.md)];
  let nearestIdx = 0;
  let nearestDist = Math.abs(stationMds[0] - caseState.pumpDepth);
  for (let i = 1; i < stationMds.length; i += 1) {
    const d = Math.abs(stationMds[i] - caseState.pumpDepth);
    if (d < nearestDist) {
      nearestDist = d;
      nearestIdx = i;
    }
  }
  const surveyEndMd = stationMds[stationMds.length - 1];
  return {
    nearestStationIndex: nearestIdx,
    nearestStationMd: stationMds[nearestIdx],
    nearestStationDistance: nearestDist,
    surveyEndMd,
    surveyToPumpDelta: surveyEndMd - caseState.pumpDepth,
    rodToPumpDelta: taperLen - caseState.pumpDepth
  };
}

export function ResultsTab({
  result,
  loading,
  error,
  lastRunAt,
  elapsedSeconds,
  caseState,
  checks,
  onNavigateTab
}: Props) {
  const primary = result?.solver;
  const fea = result?.solvers?.fea ?? null;
  const fdm = result?.solvers?.fdm ?? primary ?? null;
  const [overlayMode, setOverlayMode] = useState<OverlayMode>("depth");
  const [selectedSegment, setSelectedSegment] = useState<number | null>(null);
  const [traceabilityQuery, setTraceabilityQuery] = useState("");
  const dlsUnitLabel = caseState.unitsSelection === 1 ? "deg/100 m" : "deg/100 ft";
  const trajectorySegments = useMemo(() => buildTrajectorySegments(caseState.survey), [caseState.survey]);
  const sideLoadProfile = primary?.profiles?.sideLoadProfile ?? null;
  const pumpDiag = useMemo(() => computePumpPlacement(caseState), [caseState]);
  const filteredTraceability = useMemo(() => {
    const fields = result?.fieldTraceability?.fields ?? [];
    const q = traceabilityQuery.trim().toLowerCase();
    if (!q) return fields;
    return fields.filter((row) => {
      return (
        row.xmlKey.toLowerCase().includes(q) ||
        row.category.toLowerCase().includes(q) ||
        row.notes.toLowerCase().includes(q)
      );
    });
  }, [result?.fieldTraceability?.fields, traceabilityQuery]);

  const dynacardData = useMemo<AlignedData | null>(() => {
    const fdmSeries = toSeries(fdm ?? undefined);
    const feaSeries = toSeries(fea ?? undefined);
    if (!fdmSeries) return null;
    if (feaSeries && feaSeries.position.length === fdmSeries.position.length) {
      return [
        fdmSeries.position,
        fdmSeries.polished,
        fdmSeries.downhole,
        feaSeries.polished,
        feaSeries.downhole
      ] as AlignedData;
    }
    return [fdmSeries.position, fdmSeries.polished, fdmSeries.downhole] as AlignedData;
  }, [fdm, fea]);
  const hasCardLengthMismatch = useMemo(() => {
    const fdmSeries = toSeries(fdm ?? undefined);
    const feaSeries = toSeries(fea ?? undefined);
    if (!fdmSeries || !feaSeries) return false;
    return fdmSeries.position.length !== feaSeries.position.length;
  }, [fdm, fea]);

  const dynacardOptions = useMemo<Options>(() => {
    const seriesCount = fea ? 5 : 3;
    const seriesSpec: Options["series"] = [
      { label: "Position (m)" },
      { label: "Polished (FDM)", stroke: "#f59e0b", width: 2 },
      { label: "Downhole (FDM)", stroke: "#22d3ee", width: 2 }
    ];
    if (seriesCount === 5) {
      seriesSpec.push({ label: "Polished (FEA)", stroke: "#f43f5e", width: 1.5, dash: [6, 3] });
      seriesSpec.push({ label: "Downhole (FEA)", stroke: "#34d399", width: 1.5, dash: [6, 3] });
    }
    return {
      width: 800,
      height: 320,
      scales: { x: { time: false } },
      axes: [
        { label: "Polished-rod position (m)", stroke: "#cbd5f5" },
        { label: "Load (N)", stroke: "#cbd5f5" }
      ],
      series: seriesSpec,
      legend: { show: true }
    } satisfies Options;
  }, [fea]);

  return (
    <>
      {loading && (
        <div className="callout callout-info">
          <span className="spinner" /> Running simulation…
        </div>
      )}
      {error && (
        <div className="callout callout-error">
          <strong>Error:</strong> {error}
        </div>
      )}

      {!result && !loading && !error && (
        <div className="callout">
          No results yet. Edit inputs and press <strong>Run Solver</strong>.
        </div>
      )}
      {!!checks.issues.length && (
        <Fieldset legend="Input Integrity Checks">
          <ul className="warning-list">
            {checks.issues.map((issue) => (
              <li key={issue.code}>
                <strong>{issue.severity.toUpperCase()}:</strong> {issue.message}
              </li>
            ))}
          </ul>
        </Fieldset>
      )}
      <Fieldset legend="3D Wellbore / Rod String / Pump">
        <p className="panel-note">
          Tubing trajectory is colored by measured depth (hue). Rod string is overlaid with a
          depth gradient, and pump location is marked in red.
        </p>
        <div className="button-row">
          <button
            type="button"
            className={`btn ${overlayMode === "depth" ? "btn-primary" : ""}`}
            onClick={() => setOverlayMode("depth")}
            aria-pressed={overlayMode === "depth"}
          >
            Overlay: Depth
          </button>
          <button
            type="button"
            className={`btn ${overlayMode === "sideLoad" ? "btn-primary" : ""}`}
            onClick={() => setOverlayMode("sideLoad")}
            disabled={!sideLoadProfile?.length}
            title={!sideLoadProfile?.length ? "Run with profile output to enable side-load overlay" : ""}
            aria-pressed={overlayMode === "sideLoad"}
          >
            Overlay: Side-load risk
          </button>
          <button
            type="button"
            className="btn"
            onClick={() => setSelectedSegment(null)}
            disabled={selectedSegment === null}
            title="Clear selected trajectory segment highlight"
          >
            Clear segment highlight
          </button>
          <button
            type="button"
            className="btn"
            onClick={() => exportSvg("wellbore-3d-svg", `${caseState.wellName || "well"}_wellbore.svg`)}
            title="Export the current 3D viewer as SVG"
          >
            Export 3D SVG
          </button>
          <button
            type="button"
            className="btn"
            onClick={() =>
              exportSvgToPng("wellbore-3d-svg", `${caseState.wellName || "well"}_wellbore.png`)
            }
            title="Export the current 3D viewer as PNG"
          >
            Export 3D PNG
          </button>
          <button
            type="button"
            className="btn"
            onClick={() =>
              exportSummaryJson(
                `${caseState.wellName || "well"}_summary.json`,
                result,
                checks,
                pumpDiag
              )
            }
            title="Export current checks, diagnostics, and run metadata"
          >
            Export summary JSON
          </button>
        </div>
        <Wellbore3DView
          caseState={caseState}
          overlayMode={overlayMode}
          sideLoadProfile={sideLoadProfile}
          highlightedSegmentIndex={selectedSegment}
          onSegmentSelect={setSelectedSegment}
          svgId="wellbore-3d-svg"
        />
      </Fieldset>
      <Fieldset legend="Trajectory Analytics">
        <p className="panel-note" style={{ marginBottom: 8 }}>
          Click a row or 3D segment to cross-highlight. DLS is shown numerically only ({dlsUnitLabel}).
        </p>
        <div className="table-scroll" style={{ maxHeight: 250 }}>
          <table className="data-table">
            <thead>
              <tr>
                <th>#</th>
                <th>MD start</th>
                <th>MD end</th>
                <th>ΔMD</th>
                <th>DLS ({dlsUnitLabel})</th>
              </tr>
            </thead>
            <tbody>
              {trajectorySegments.map((segment) => (
                <tr
                  key={segment.index}
                  className={selectedSegment === segment.index ? "row-selected" : ""}
                  onClick={() => setSelectedSegment(segment.index)}
                  onKeyDown={(event) => {
                    if (event.key === "Enter" || event.key === " ") {
                      event.preventDefault();
                      setSelectedSegment(segment.index);
                    }
                  }}
                  role="button"
                  tabIndex={0}
                  aria-label={`Select trajectory segment ${segment.index + 1}`}
                  style={{ cursor: "pointer" }}
                >
                  <td>{segment.index + 1}</td>
                  <td>{segment.a.md.toFixed(1)}</td>
                  <td>{segment.b.md.toFixed(1)}</td>
                  <td>{segment.dMd.toFixed(1)}</td>
                  <td>{segment.dls.toFixed(2)}</td>
                </tr>
              ))}
              {!trajectorySegments.length && (
                <tr>
                  <td colSpan={5} className="empty-row">No trajectory segments to display.</td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </Fieldset>
      <Fieldset legend="Pump Placement Diagnostics">
        <div className="kpi-grid">
          <Kpi
            label="Nearest station index"
            value={pumpDiag.nearestStationIndex >= 0 ? String(pumpDiag.nearestStationIndex + 1) : "—"}
          />
          <Kpi
            label="Nearest station MD"
            value={pumpDiag.nearestStationMd !== null ? pumpDiag.nearestStationMd.toFixed(1) : "—"}
          />
          <Kpi
            label="Pump-to-nearest station ΔMD"
            value={
              pumpDiag.nearestStationDistance !== null
                ? `${pumpDiag.nearestStationDistance.toFixed(1)}`
                : "—"
            }
          />
          <Kpi
            label="Survey end - pump ΔMD"
            value={pumpDiag.surveyToPumpDelta !== null ? pumpDiag.surveyToPumpDelta.toFixed(1) : "—"}
          />
          <Kpi label="Rod total - pump Δ" value={pumpDiag.rodToPumpDelta.toFixed(1)} />
          <Kpi
            label="Tubing anchor - pump Δ"
            value={(caseState.tubingAnchorLocation - caseState.pumpDepth).toFixed(1)}
          />
        </div>
        <div className="button-row">
          <button type="button" className="btn" onClick={() => onNavigateTab?.("tab-trajectory")}>
            Go to Trajectory tab
          </button>
          <button type="button" className="btn" onClick={() => onNavigateTab?.("tab-rod")}>
            Go to Rod String tab
          </button>
          <button type="button" className="btn" onClick={() => onNavigateTab?.("tab-well")}>
            Go to Well tab
          </button>
        </div>
      </Fieldset>

      {result && (
        <>
          <Fieldset legend="Summary — Key Performance Indicators">
            <div className="kpi-grid">
              <Kpi label="Active solver" value={(result.runMetadata.solverModel || "fdm").toUpperCase()} />
              <Kpi label="Workflow" value={result.runMetadata.workflow ?? "predictive"} />
              <Kpi label="Schema" value={`v${result.schemaVersion ?? 2}`} />
              <Kpi label="Last run" value={lastRunAt ?? "—"} />
              <Kpi label="Elapsed" value={elapsedSeconds !== null ? `${elapsedSeconds.toFixed(1)} s` : "—"} />
              <Kpi label="Well" value={result.parsed.model.wellName} />
              <Kpi label="Surface peak (kN)" value={formatKn(primary?.maxPolishedLoad)} />
              <Kpi label="Surface min (kN)" value={formatKn(primary?.minPolishedLoad)} />
              <Kpi label="Downhole peak (kN)" value={formatKn(primary?.maxDownholeLoad)} />
              <Kpi label="Downhole min (kN)" value={formatKn(primary?.minDownholeLoad)} />
              <Kpi label="Point count" value={String(primary?.pointCount ?? "—")} />
              <Kpi
                label="Gas interference"
                value={primary?.gasInterference ? "Yes" : "No"}
              />
            </div>
          </Fieldset>

          <Fieldset legend="Dynamometer Card">
            {hasCardLengthMismatch && (
              <div className="callout">
                FDM and FEA card lengths differ; chart overlay is limited to FDM series for this run.
              </div>
            )}
            {dynacardData ? (
              <UPlotChart data={dynacardData} options={dynacardOptions} height={340} />
            ) : (
              <p className="panel-note">No card data in response.</p>
            )}
            {primary?.fourierBaseline && primary.fourierBaseline.card?.length ? (
              <div style={{ marginTop: 16 }}>
                <p className="panel-note">
                  Fourier baseline ({primary.fourierBaseline.harmonics} harmonics): RMS polished{" "}
                  {primary.fourierBaseline.residualRmsPolished?.toFixed(1)} N · RMS downhole{" "}
                  {primary.fourierBaseline.residualRmsDownhole?.toFixed(1)} N
                </p>
                <UPlotChart
                  height={260}
                  data={(() => {
                    const n = Math.min(primary.card.length, primary.fourierBaseline.card.length);
                    const pos = primary.card.slice(0, n).map((p) => p.position);
                    const pol = primary.card.slice(0, n).map((p) => p.polishedLoad);
                    const four = primary.fourierBaseline.card.slice(0, n).map((p) => p.polishedLoad);
                    return [pos, pol, four];
                  })()}
                  options={{
                    width: 800,
                    height: 260,
                    scales: { x: { time: false } },
                    axes: [
                      { label: "Position (m)", stroke: "#cbd5f5" },
                      { label: "Load (N)", stroke: "#cbd5f5" }
                    ],
                    series: [
                      { label: "Position" },
                      { label: "Polished (FDM)", stroke: "#f59e0b", width: 2 },
                      { label: "Polished (Fourier)", stroke: "#a78bfa", width: 1.5, dash: [8, 4] }
                    ],
                    legend: { show: true }
                  }}
                />
              </div>
            ) : null}
          </Fieldset>

          {primary?.profiles && (
            <Fieldset legend="Profiles (trajectory / side-load / friction)">
              <div className="kpi-grid">
                <Kpi label="Profile nodes" value={String(primary.profiles.nodeCount)} />
                <Kpi
                  label="Max |side load| (N)"
                  value={
                    primary.profiles.sideLoadProfile.length > 0
                      ? Math.max(
                          0,
                          ...primary.profiles.sideLoadProfile.map((v) => Math.abs(v))
                        ).toFixed(0)
                      : "—"
                  }
                />
                <Kpi
                  label="Max |friction| (N)"
                  value={
                    primary.profiles.frictionProfile.length > 0
                      ? Math.max(
                          0,
                          ...primary.profiles.frictionProfile.map((v) => Math.abs(v))
                        ).toFixed(0)
                      : "—"
                  }
                />
              </div>
            </Fieldset>
          )}

          {primary?.diagnostics &&
            primary.diagnostics.chamberPressurePa.length > 0 &&
            primary.diagnostics.gasFraction.length > 0 && (
            <Fieldset legend="Diagnostics (valve / chamber / gas)">
              <div className="kpi-grid">
                <Kpi
                  label="Valve samples"
                  value={`${primary.diagnostics.valveStates.length} points`}
                />
                <Kpi
                  label="Chamber P range (Pa)"
                  value={`${Math.min(...primary.diagnostics.chamberPressurePa).toFixed(0)} … ${Math.max(...primary.diagnostics.chamberPressurePa).toFixed(0)}`}
                />
                <Kpi
                  label="Gas fraction range"
                  value={`${Math.min(...primary.diagnostics.gasFraction).toFixed(3)} … ${Math.max(...primary.diagnostics.gasFraction).toFixed(3)}`}
                />
              </div>
            </Fieldset>
          )}

          {result.fieldTraceability?.fields?.length ? (
            <Fieldset legend="MVP field traceability (static map)">
              <p className="panel-note">
                Categories mirror <code>docs/engineering/field-traceability.md</code> (
                physics, metadata, parseCalibration, payloadInactive, parsedUnused).
              </p>
              <div className="button-row" style={{ gap: 8, alignItems: "center" }}>
                <input
                  className="panel-input"
                  value={traceabilityQuery}
                  placeholder="Filter by XML key/category/notes"
                  aria-label="Filter traceability rows"
                  onChange={(e) => setTraceabilityQuery(e.target.value)}
                />
                <span className="panel-note">
                  Showing {filteredTraceability.length} / {result.fieldTraceability.fields.length}
                </span>
              </div>
              <div className="table-scroll" style={{ maxHeight: 280 }}>
                <table className="data-table">
                  <thead>
                    <tr>
                      <th>XML field</th>
                      <th>Category</th>
                      <th>In file</th>
                      <th>Notes</th>
                    </tr>
                  </thead>
                  <tbody>
                    {filteredTraceability.map((row) => (
                      <tr key={row.xmlKey} title={row.notes || undefined}>
                        <td className="mono">{row.xmlKey}</td>
                        <td>{row.category}</td>
                        <td>{row.presentInXml ? "yes" : "—"}</td>
                        <td>{row.notes}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </Fieldset>
          ) : null}

          {result.comparison && (
            <Fieldset legend="FDM vs FEA Comparison">
              <div className="kpi-grid">
                <Kpi
                  label="ΔPolished max (N)"
                  value={result.comparison.polishedMaxDelta.toFixed(2)}
                />
                <Kpi
                  label="ΔPolished min (N)"
                  value={result.comparison.polishedMinDelta.toFixed(2)}
                />
                <Kpi
                  label="ΔDownhole max (N)"
                  value={result.comparison.downholeMaxDelta.toFixed(2)}
                />
                <Kpi
                  label="ΔDownhole min (N)"
                  value={result.comparison.downholeMinDelta.toFixed(2)}
                />
                <Kpi
                  label="Residual RMS (N)"
                  value={result.comparison.residualSummary?.rms.toFixed(2) ?? "—"}
                />
                <Kpi
                  label="Residual points"
                  value={String(result.comparison.residualSummary?.points ?? "—")}
                />
              </div>
            </Fieldset>
          )}

          {(result.parseWarnings?.length || primary?.warnings?.length) && (
            <Fieldset legend="Warnings">
              <ul className="warning-list">
                {(result.parseWarnings ?? []).map((w, i) => (
                  <li key={`pw-${i}`}>{w}</li>
                ))}
                {(primary?.warnings ?? []).map((w, i) => (
                  <li key={`sw-${i}`}>{w}</li>
                ))}
              </ul>
            </Fieldset>
          )}

          {result.parsed.unsupportedFields?.length ? (
            <Fieldset legend={`Unsupported XML fields (${result.parsed.unsupportedFields.length})`}>
              <p className="panel-note">
                Preserved on export; not consumed by the solver.
              </p>
              <pre className="mono-block">
                {result.parsed.unsupportedFields.join("\n")}
              </pre>
            </Fieldset>
          ) : null}
        </>
      )}
    </>
  );
}

function exportSvg(svgId: string, filename: string) {
  const svg = document.getElementById(svgId);
  if (!svg) return;
  const serializer = new XMLSerializer();
  const source = serializer.serializeToString(svg);
  const blob = new Blob([source], { type: "image/svg+xml;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
}

function exportSvgToPng(svgId: string, filename: string) {
  const svg = document.getElementById(svgId) as SVGSVGElement | null;
  if (!svg) return;
  const serializer = new XMLSerializer();
  const source = serializer.serializeToString(svg);
  const svgBlob = new Blob([source], { type: "image/svg+xml;charset=utf-8" });
  const url = URL.createObjectURL(svgBlob);
  const img = new Image();
  img.onload = () => {
    const vb = svg.viewBox.baseVal;
    const width = vb.width || svg.clientWidth || 800;
    const height = vb.height || svg.clientHeight || 400;
    const canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext("2d");
    if (!ctx) {
      URL.revokeObjectURL(url);
      return;
    }
    ctx.drawImage(img, 0, 0);
    canvas.toBlob((blob) => {
      if (!blob) return;
      const pngUrl = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = pngUrl;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      a.remove();
      URL.revokeObjectURL(pngUrl);
    }, "image/png");
    URL.revokeObjectURL(url);
  };
  img.src = url;
}

function exportSummaryJson(
  filename: string,
  result: SolveResponse | null,
  checks: EngineeringChecks,
  pumpDiag: ReturnType<typeof computePumpPlacement>
) {
  const payload = {
    exportedAt: new Date().toISOString(),
    checks,
    pumpPlacement: pumpDiag,
    runMetadata: result?.runMetadata ?? null,
    comparison: result?.comparison ?? null,
    warnings: {
      parse: result?.parseWarnings ?? [],
      solver: result?.solver?.warnings ?? []
    }
  };
  const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
}

function Kpi({ label, value }: { label: string; value: string }) {
  return (
    <div className="kpi-cell">
      <div className="kpi-label">{label}</div>
      <div className="kpi-val">{value}</div>
    </div>
  );
}
